mirror of
				https://github.com/HabitRPG/habitica.git
				synced 2025-10-29 04:04:47 +01:00 
			
		
		
		
	Compare commits
	
		
			186 Commits
		
	
	
		
	
	| Author | SHA1 | Date | |
|---|---|---|---|
|  | 3cf7b2c96c | ||
|  | 286db39478 | ||
|  | 4d4c1cfaf3 | ||
|  | d7ad3efabf | ||
|  | f8876fe055 | ||
|  | b973335d69 | ||
|  | 3b6fce0708 | ||
|  | ff6bd6de71 | ||
|  | 042afe1df3 | ||
|  | a208ba4aba | ||
|  | 0e958fd306 | ||
|  | d98fe79e9c | ||
|  | 0e5a811b98 | ||
|  | a28aea65f8 | ||
|  | 0f92349902 | ||
|  | d4881cb73a | ||
|  | b3216fdb85 | ||
|  | 3e37941e0a | ||
|  | 32088767ac | ||
|  | f4d021ab8c | ||
|  | 8532203717 | ||
|  | 365daba6fc | ||
|  | 70692752c7 | ||
|  | 95d4016678 | ||
|  | 061457b268 | ||
|  | e7ec9a6d65 | ||
|  | d1396e7bc6 | ||
|  | d5cedaa925 | ||
|  | bea813b318 | ||
|  | b31268fbc2 | ||
|  | 35727228f0 | ||
|  | feb7ab8345 | ||
|  | f4422b8d6c | ||
|  | 2d4dc9e23c | ||
|  | bba2e71af3 | ||
|  | 26123ac6ae | ||
|  | addee73e4d | ||
|  | c39505d41c | ||
|  | 9736ef0d25 | ||
|  | 638259b885 | ||
|  | c24545cae5 | ||
|  | 6bc70ca471 | ||
|  | d2f0d7b20b | ||
|  | b445bc8261 | ||
|  | 31281b43d3 | ||
|  | a8a915ea8e | ||
|  | dc3ee25e65 | ||
|  | 3c9f7ff9d8 | ||
|  | 54f57445da | ||
|  | 95ef2b1789 | ||
|  | 4d32977e5c | ||
|  | 7fe2504906 | ||
|  | b74cee3d21 | ||
|  | 5af7733150 | ||
|  | 824bf62e0a | ||
|  | ac24a5dddd | ||
|  | 9111f59da4 | ||
|  | 6a550b34df | ||
|  | 0c973b1cf0 | ||
|  | 4170ef5e79 | ||
|  | 506275c30e | ||
|  | 6838b7d0a6 | ||
|  | e987cd52a6 | ||
|  | 1df8668d38 | ||
|  | 1af42aa7fe | ||
|  | 9dc9faa70d | ||
|  | 7fbcc0a263 | ||
|  | 0450e9c3ae | ||
|  | 15626b8ae1 | ||
|  | 444f393f3a | ||
|  | d711bf4085 | ||
|  | 7b93e326fc | ||
|  | c62ea522c0 | ||
|  | 810362a404 | ||
|  | 2e429513da | ||
|  | c8625cb23f | ||
|  | ad50f90ba0 | ||
|  | d5305f74e3 | ||
|  | 5ea20e4c8c | ||
|  | 9e4e2d0b34 | ||
|  | 6456e6b8e3 | ||
|  | af961ff16c | ||
|  | eb2a6095c2 | ||
|  | ee82f5a774 | ||
|  | f1c8c4c54b | ||
|  | 79b15d7ddf | ||
|  | f2fed7ea39 | ||
|  | ca81ff5af6 | ||
|  | e28c214696 | ||
|  | 792f45e9bd | ||
|  | a69c0999d3 | ||
|  | 37ff35306c | ||
|  | fd430e95b2 | ||
|  | 0ea472b3af | ||
|  | 6d0496fbd0 | ||
|  | fac1889776 | ||
|  | 9ca6da0f75 | ||
|  | 99f50f825a | ||
|  | 2308f2397e | ||
|  | f10d5110e5 | ||
|  | 5ced47f590 | ||
|  | 24b2bc9aa1 | ||
|  | aea08a971a | ||
|  | e3452b3ba7 | ||
|  | 3db666eb9e | ||
|  | cf5985e38c | ||
|  | d88a8247d1 | ||
|  | 7e1ae6a571 | ||
|  | d117117885 | ||
|  | 5dd168eee4 | ||
|  | a7c9355dd5 | ||
|  | 81d3e8a68f | ||
|  | e0fbfffbf2 | ||
|  | 36b968a74a | ||
|  | fff249fb00 | ||
|  | ce5c6b9517 | ||
|  | 702013f9ed | ||
|  | 3503b307b2 | ||
|  | f1ec8bbf2c | ||
|  | 58b033db9e | ||
|  | b0dcc2f6ef | ||
|  | 3377f8a916 | ||
|  | 22f83d09c4 | ||
|  | 496534ab4b | ||
|  | 7325d2020e | ||
|  | 1a0d39e566 | ||
|  | dc62ab7577 | ||
|  | c4e5633e48 | ||
|  | d977656e96 | ||
|  | b0a980d56e | ||
|  | 3d75c99f8d | ||
|  | 0aa16d7021 | ||
|  | 4fa3046104 | ||
|  | fda4be01b8 | ||
|  | 67436fbef1 | ||
|  | ffb92a5faa | ||
|  | 4a44d60fac | ||
|  | 122cc510d3 | ||
|  | 29a7a07d14 | ||
|  | 18783aefe3 | ||
|  | a5e242759c | ||
|  | e01c6cc9a6 | ||
|  | 8a75383c43 | ||
|  | 47ebee9ae8 | ||
|  | 4e97355110 | ||
|  | 7045b5c214 | ||
|  | e41dccf6d5 | ||
|  | d0815ea9ee | ||
|  | d0bd62bf02 | ||
|  | 39d7581c6c | ||
|  | fdf146dd01 | ||
|  | 023b3ffd88 | ||
|  | f01e552637 | ||
|  | 9f11820a02 | ||
|  | ca6c7b8e5f | ||
|  | 02f8ba1638 | ||
|  | 8eb7c67f12 | ||
|  | 1f895fda44 | ||
|  | e87c180e9b | ||
|  | dbf9fd54be | ||
|  | c9b99d1ecf | ||
|  | fd8120c80d | ||
|  | 053e75562f | ||
|  | 1d50027f51 | ||
|  | 7fe74fd06a | ||
|  | 173a8561b6 | ||
|  | f21bef707b | ||
|  | 9cf2ccf7c4 | ||
|  | 77d75c4669 | ||
|  | 1c17b95146 | ||
|  | d89fc209d1 | ||
|  | 844c8bb3e6 | ||
|  | 569fb11db8 | ||
|  | 7671347d3a | ||
|  | dc3a02bc2e | ||
|  | 1d8c126687 | ||
|  | 7ee49a43f2 | ||
|  | 900bc8dfc1 | ||
|  | ec260016d3 | ||
|  | ec770fb29e | ||
|  | c1079e4eae | ||
|  | 3cb5637fd5 | ||
|  | cf3a0118c9 | ||
|  | 895a383089 | ||
|  | f730e7b345 | ||
|  | d8f3d99d59 | 
| @@ -20,11 +20,7 @@ container_commands: | ||||
|     command: "touch /tmp/.babel.json" | ||||
|   02_ownBabel: | ||||
|     command: "chmod a+rw /tmp/.babel.json" | ||||
|   03_installBower: | ||||
|     command: "$NODE_HOME/bin/npm install -g bower" | ||||
|   04_installGulp: | ||||
|   03_installGulp: | ||||
|     command: "$NODE_HOME/bin/npm install -g gulp" | ||||
|   05_runBower: | ||||
|     command: "$NODE_HOME/lib/node_modules/bower/bin/bower --config.interactive=false --allow-root install -f" | ||||
|   06_runGulp: | ||||
|   04_runGulp: | ||||
|     command: "$NODE_HOME/lib/node_modules/gulp/bin/gulp.js build" | ||||
|   | ||||
| @@ -16,5 +16,3 @@ migrations/* | ||||
| scripts/* | ||||
| website/common/browserify.js | ||||
| Gruntfile.js | ||||
| gulpfile.js | ||||
| gulp | ||||
							
								
								
									
										2
									
								
								.github/CONTRIBUTING.md
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										2
									
								
								.github/CONTRIBUTING.md
									
									
									
									
										vendored
									
									
								
							| @@ -4,7 +4,7 @@ | ||||
|  | ||||
| # Pull Request | ||||
|  | ||||
| [Please see these instructions for adding a pull request](http://habitica.wikia.com/wiki/Using_Habitica_Git#Pull_Request) | ||||
| [Please see these instructions for adding a pull request](http://habitica.wikia.com/wiki/Using_Your_Local_Install_to_Modify_Habitica%27s_Website_and_API) | ||||
|  | ||||
| # Requesting a feature | ||||
|  | ||||
|   | ||||
							
								
								
									
										2
									
								
								.github/ISSUE_TEMPLATE.md
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										2
									
								
								.github/ISSUE_TEMPLATE.md
									
									
									
									
										vendored
									
									
								
							| @@ -6,7 +6,7 @@ | ||||
|  | ||||
| [//]: # (For more guidelines see https://github.com/HabitRPG/habitica/issues/2760) | ||||
|  | ||||
| [//]: # (Fill out relevant information - UUID is found in Settings -> API) | ||||
| [//]: # (Fill out relevant information - UUID is found from the Habitia website at User Icon > Settings > API) | ||||
| ### General Info | ||||
|   * UUID:  | ||||
|   * Browser:  | ||||
|   | ||||
							
								
								
									
										4
									
								
								.github/PULL_REQUEST_TEMPLATE.md
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										4
									
								
								.github/PULL_REQUEST_TEMPLATE.md
									
									
									
									
										vendored
									
									
								
							| @@ -1,4 +1,4 @@ | ||||
| [//]: # (Note: See http://habitica.wikia.com/wiki/Using_Habitica_Git#Pull_Request for more info) | ||||
| [//]: # (Note: See http://habitica.wikia.com/wiki/Using_Your_Local_Install_to_Modify_Habitica%27s_Website_and_API for more info) | ||||
|  | ||||
| [//]: # (Put Issue # or URL here, if applicable. This will automatically close the issue if your PR is merged in) | ||||
| Fixes put_issue_url_here | ||||
| @@ -8,7 +8,7 @@ Fixes put_issue_url_here | ||||
|  | ||||
|  | ||||
|  | ||||
| [//]: # (Put User ID in here - found in Settings -> API) | ||||
| [//]: # (Put User ID in here - found on the Habitica website at User Icon > Settings > API) | ||||
|  | ||||
| ---- | ||||
| UUID:  | ||||
|   | ||||
							
								
								
									
										3
									
								
								.gitignore
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										3
									
								
								.gitignore
									
									
									
									
										vendored
									
									
								
							| @@ -2,11 +2,14 @@ | ||||
| website/client-old/gen | ||||
| website/client-old/common | ||||
| website/client-old/apidoc | ||||
| website/build | ||||
| website/client-old/js/habitrpg-shared.js* | ||||
| website/client-old/css/habitrpg-shared.css | ||||
| website/transpiled-babel/ | ||||
| website/common/transpiled-babel/ | ||||
| node_modules | ||||
| content_cache | ||||
| apidoc_build | ||||
| *.swp | ||||
| .idea* | ||||
| config.json | ||||
|   | ||||
| @@ -34,5 +34,4 @@ env: | ||||
|     - TEST="test:sanity" | ||||
|     - TEST="test:content" COVERAGE=true | ||||
|     - TEST="test:common" COVERAGE=true | ||||
|     - TEST="client:unit" COVERAGE=true | ||||
|     - TEST="apidoc" | ||||
|   | ||||
| @@ -4,7 +4,7 @@ FROM node:boron | ||||
| # The used solution is suggested here https://github.com/npm/npm/issues/16807#issuecomment-313591975 | ||||
| RUN yarn global add npm@5 | ||||
| # Install global packages | ||||
| RUN npm install -g gulp grunt-cli bower mocha | ||||
| RUN npm install -g gulp mocha | ||||
|  | ||||
| # Clone Habitica repo and install dependencies | ||||
| RUN mkdir -p /usr/src/habitrpg | ||||
| @@ -12,7 +12,6 @@ WORKDIR /usr/src/habitrpg | ||||
| RUN git clone https://github.com/HabitRPG/habitica.git /usr/src/habitrpg | ||||
| RUN cp config.json.example config.json | ||||
| RUN npm install | ||||
| RUN bower install --allow-root | ||||
|  | ||||
| # Create Build dir | ||||
| RUN mkdir -p ./website/build | ||||
|   | ||||
| @@ -1,17 +1,27 @@ | ||||
| FROM node:boron | ||||
|  | ||||
| ENV ADMIN_EMAIL admin@habitica.com | ||||
| ENV AMAZON_PAYMENTS_CLIENT_ID amzn1.application-oa2-client.68ed9e6904ef438fbc1bf86bf494056e | ||||
| ENV AMAZON_PAYMENTS_SELLER_ID AMQ3SB4SG5E91 | ||||
| ENV AMPLITUDE_KEY e8d4c24b3d6ef3ee73eeba715023dd43 | ||||
| ENV BASE_URL https://habitica.com | ||||
| ENV FACEBOOK_KEY 128307497299777 | ||||
| ENV GA_ID UA-33510635-1 | ||||
| ENV GOOGLE_CLIENT_ID 1035232791481-32vtplgnjnd1aufv3mcu1lthf31795fq.apps.googleusercontent.com | ||||
| ENV NODE_ENV production | ||||
| ENV STRIPE_PUB_KEY pk_85fQ0yMECHNfHTSsZoxZXlPSwSNfA | ||||
|  | ||||
| # Upgrade NPM to v5 (Yarn is needed because of this bug https://github.com/npm/npm/issues/16807) | ||||
| # The used solution is suggested here https://github.com/npm/npm/issues/16807#issuecomment-313591975 | ||||
| RUN yarn global add npm@5 | ||||
| # Install global packages | ||||
| RUN npm install -g gulp grunt-cli bower mocha | ||||
| RUN npm install -g gulp mocha | ||||
|  | ||||
| # Clone Habitica repo and install dependencies | ||||
| RUN mkdir -p /usr/src/habitrpg | ||||
| WORKDIR /usr/src/habitrpg | ||||
| RUN git clone --branch v4.0.3 https://github.com/HabitRPG/habitica.git /usr/src/habitrpg | ||||
| RUN git clone --branch v4.6.3 https://github.com/HabitRPG/habitica.git /usr/src/habitrpg | ||||
| RUN npm install | ||||
| RUN bower install --allow-root | ||||
| RUN gulp build:prod --force | ||||
|  | ||||
| # Create Build dir | ||||
|   | ||||
							
								
								
									
										142
									
								
								Gruntfile.js
									
									
									
									
									
								
							
							
						
						
									
										142
									
								
								Gruntfile.js
									
									
									
									
									
								
							| @@ -1,142 +0,0 @@ | ||||
| /*global module:false*/ | ||||
| require('babel-register'); | ||||
| var _ = require('lodash'); | ||||
| module.exports = function(grunt) { | ||||
|  | ||||
|   // Project configuration. | ||||
|   grunt.initConfig({ | ||||
|     pkg: grunt.file.readJSON('package.json'), | ||||
|  | ||||
|     karma: { | ||||
|       unit: { | ||||
|         configFile: 'test/client-old/spec/karma.conf.js' | ||||
|       }, | ||||
|       continuous: { | ||||
|         configFile: 'test/client-old/spec/karma.conf.js', | ||||
|         singleRun: true, | ||||
|         autoWatch: false | ||||
|       } | ||||
|     }, | ||||
|  | ||||
|     clean: { | ||||
|       build: ['website/build'] | ||||
|     }, | ||||
|  | ||||
|     cssmin: { | ||||
|       dist: { | ||||
|         options: { | ||||
|           report: 'gzip' | ||||
|         }, | ||||
|         files:{ | ||||
|           "website/client-old/css/habitrpg-shared.css": [ | ||||
|             "website/assets/sprites/dist/spritesmith*.css", | ||||
|             "website/assets/sprites/css/backer.css", | ||||
|             "website/assets/sprites/css/Mounts.css", | ||||
|             "website/assets/sprites/css/index.css" | ||||
|           ] | ||||
|         } | ||||
|       } | ||||
|     }, | ||||
|  | ||||
|     stylus: { | ||||
|       build: { | ||||
|         options: { | ||||
|           compress: false, // AFTER | ||||
|           'include css': true, | ||||
|           paths: ['website/client-old'] | ||||
|         }, | ||||
|         files: { | ||||
|           'website/build/app.css': ['website/client-old/css/index.styl'], | ||||
|           'website/build/static.css': ['website/client-old/css/static.styl'] | ||||
|         } | ||||
|       } | ||||
|     }, | ||||
|  | ||||
|     copy: { | ||||
|       build: { | ||||
|         files: [ | ||||
|           {expand: true, cwd: 'website/client-old/', src: 'favicon.ico', dest: 'website/build/'}, | ||||
|           {expand: true, cwd: 'website/client-old/', src: 'favicon_192x192.png', dest: 'website/build/'}, | ||||
|           {expand: true, cwd: 'website/assets/sprites/dist/', src: 'spritesmith*.png', dest: 'website/build/static/sprites'}, | ||||
|           {expand: true, cwd: 'website/assets/sprites/', src: 'backer-only/*.gif', dest: 'website/build/'}, | ||||
|           {expand: true, cwd: 'website/assets/sprites/', src: 'npc_ian.gif', dest: 'website/build/'}, | ||||
|           {expand: true, cwd: 'website/assets/sprites/', src: 'quest_*.gif', dest: 'website/build/'}, | ||||
|           {expand: true, cwd: 'website/client-old/', src: 'bower_components/bootstrap/dist/fonts/*', dest: 'website/build/'} | ||||
|         ] | ||||
|       } | ||||
|     }, | ||||
|  | ||||
|     // UPDATE IT WHEN YOU ADD SOME FILES NOT ALREADY MATCHED! | ||||
|     hashres: { | ||||
|       build: { | ||||
|         options: { | ||||
|           fileNameFormat: '${name}-${hash}.${ext}' | ||||
|         }, | ||||
|         src: [ | ||||
|           'website/build/*.js', | ||||
|           'website/build/*.css', | ||||
|           'website/build/favicon.ico', | ||||
|           'website/build/favicon_192x192.png', | ||||
|           'website/build/*.png', | ||||
|           'website/build/static/sprites/*.png', | ||||
|           'website/build/*.gif', | ||||
|           'website/build/bower_components/bootstrap/dist/fonts/*' | ||||
|         ], | ||||
|         dest: 'website/build/*.css' | ||||
|       } | ||||
|     } | ||||
|   }); | ||||
|  | ||||
|   //Load build files from client-old/manifest.json | ||||
|   grunt.registerTask('loadManifestFiles', 'Load all build files from client-old/manifest.json', function(){ | ||||
|     var files = grunt.file.readJSON('./website/client-old/manifest.json'); | ||||
|     var uglify = {}; | ||||
|     var cssmin = {}; | ||||
|  | ||||
|     _.each(files, function(val, key){ | ||||
|  | ||||
|       var js = uglify['website/build/' + key + '.js'] = []; | ||||
|  | ||||
|       _.each(files[key].js, function(val){ | ||||
|         var path = "./"; | ||||
|         if( val.indexOf('common/') == -1) | ||||
|           path = './website/client-old/'; | ||||
|         js.push(path + val); | ||||
|       }); | ||||
|  | ||||
|       var css = cssmin['website/build/' + key + '.css'] = []; | ||||
|  | ||||
|       _.each(files[key].css, function(val){ | ||||
|         var path = "./"; | ||||
|         if( val.indexOf('common/') == -1) { | ||||
|           path = (val == 'app.css' || val == 'static.css') ?  './website/build/' : './website/client-old/'; | ||||
|         } | ||||
|         css.push(path + val) | ||||
|       }); | ||||
|  | ||||
|     }); | ||||
|  | ||||
|     grunt.config.set('uglify.build.files', uglify); | ||||
|     grunt.config.set('uglify.build.options', {compress: false}); | ||||
|  | ||||
|     grunt.config.set('cssmin.build.files', cssmin); | ||||
|     // Rewrite urls to relative path | ||||
|     grunt.config.set('cssmin.build.options', {'target': 'website/client-old/css/whatever-css.css'}); | ||||
|   }); | ||||
|  | ||||
|   // Register tasks. | ||||
|   grunt.registerTask('build:prod', ['loadManifestFiles', 'clean:build', 'uglify', 'stylus', 'cssmin', 'copy:build', 'hashres']); | ||||
|   grunt.registerTask('build:dev', ['cssmin', 'stylus']); | ||||
|   grunt.registerTask('build:test', ['build:dev']); | ||||
|  | ||||
|   // Load tasks | ||||
|   grunt.loadNpmTasks('grunt-contrib-uglify'); | ||||
|   grunt.loadNpmTasks('grunt-contrib-clean'); | ||||
|   grunt.loadNpmTasks('grunt-contrib-stylus'); | ||||
|   grunt.loadNpmTasks('grunt-contrib-cssmin'); | ||||
|   grunt.loadNpmTasks('grunt-contrib-copy'); | ||||
|   grunt.loadNpmTasks('grunt-contrib-watch'); | ||||
|   grunt.loadNpmTasks('grunt-hashres'); | ||||
|   if (process.env.NODE_ENV !== 'production') grunt.loadNpmTasks('grunt-karma'); | ||||
|  | ||||
| }; | ||||
							
								
								
									
										56
									
								
								bower.json
									
									
									
									
									
								
							
							
						
						
									
										56
									
								
								bower.json
									
									
									
									
									
								
							| @@ -1,56 +0,0 @@ | ||||
| { | ||||
|   "name": "HabitRPG", | ||||
|   "version": "0.1.1", | ||||
|   "homepage": "https://github.com/lefnire/habitrpg", | ||||
|   "authors": [ | ||||
|     "Tyler Renelle <tylerrenelle@gmail.com>" | ||||
|   ], | ||||
|   "private": true, | ||||
|   "ignore": [ | ||||
|     "**/.*", | ||||
|     "node_modules", | ||||
|     "website/client-old/bower_components", | ||||
|     "test", | ||||
|     "tests" | ||||
|   ], | ||||
|   "dependencies": { | ||||
|     "Angular-At-Directive": "snicker/Angular-At-Directive#c27bae207aa06d1e", | ||||
|     "angular": "1.3.9", | ||||
|     "angular-bootstrap": "0.13.0", | ||||
|     "angular-filter": "0.5.1", | ||||
|     "angular-loading-bar": "0.6.0", | ||||
|     "angular-resource": "1.3.9", | ||||
|     "angular-sanitize": "1.3.9", | ||||
|     "angular-ui": "0.4.0", | ||||
|     "angular-ui-router": "0.2.13", | ||||
|     "angular-ui-select2": "angular-ui/ui-select2#afa6589a54cb72815f", | ||||
|     "angular-ui-utils": "0.1.0", | ||||
|     "bootstrap": "3.1.0", | ||||
|     "bootstrap-growl": "ifightcrime/bootstrap-growl#162daa41cd1155f", | ||||
|     "bootstrap-tour": "0.10.1", | ||||
|     "css-social-buttons": "samcollins/css-social-buttons#v1.1.1 ", | ||||
|     "github-buttons": "mdo/github-buttons#v3.0.0", | ||||
|     "hello": "1.14.1", | ||||
|     "jquery": "2.1.0", | ||||
|     "jquery-colorbox": "1.4.36", | ||||
|     "jquery-ui": "1.10.3", | ||||
|     "jquery.cookie": "1.4.0", | ||||
|     "js-emoji": "snicker/js-emoji#f25d8a303f", | ||||
|     "ngInfiniteScroll": "1.1.0", | ||||
|     "pnotify": "1.3.1", | ||||
|     "sticky": "1.0.3", | ||||
|     "swagger-ui": "wordnik/swagger-ui#v2.0.24", | ||||
|     "smart-app-banner": "78ef9c0679723b25be1a0ae04f7b4aef7cbced4f", | ||||
|     "habitica-markdown": "1.2.2", | ||||
|     "pusher-js-auth": "^2.0.0", | ||||
|     "pusher-websocket-iso": "pusher#^3.2.0", | ||||
|     "taggle": "^1.11.1" | ||||
|   }, | ||||
|   "devDependencies": { | ||||
|     "angular-mocks": "1.3.9" | ||||
|   }, | ||||
|   "resolutions": { | ||||
|     "angular": "1.3.9", | ||||
|     "jquery": ">=1.9.0" | ||||
|   } | ||||
| } | ||||
| @@ -1,10 +0,0 @@ | ||||
| { | ||||
|   "root": true, | ||||
|   "env": { | ||||
|     "node": true, | ||||
|   }, | ||||
|   "extends": [ | ||||
|     "habitrpg/server", | ||||
|     "habitrpg/babel" | ||||
|   ], | ||||
| } | ||||
| @@ -2,7 +2,7 @@ import gulp from 'gulp'; | ||||
| import clean from 'rimraf'; | ||||
| import apidoc from 'apidoc'; | ||||
|  | ||||
| const APIDOC_DEST_PATH = './website/build/apidoc'; | ||||
| const APIDOC_DEST_PATH = './apidoc_build'; | ||||
| const APIDOC_SRC_PATH = './website/server'; | ||||
| gulp.task('apidoc:clean', (done) => { | ||||
|   clean(APIDOC_DEST_PATH, done); | ||||
| @@ -22,5 +22,5 @@ gulp.task('apidoc', ['apidoc:clean'], (done) => { | ||||
| }); | ||||
|  | ||||
| gulp.task('apidoc:watch', ['apidoc'], () => { | ||||
|   return gulp.watch(APIDOC_SRC_PATH + '/**/*.js', ['apidoc']); | ||||
|   return gulp.watch(`${APIDOC_SRC_PATH}/**/*.js`, ['apidoc']); | ||||
| }); | ||||
|   | ||||
| @@ -1,31 +0,0 @@ | ||||
| import gulp from 'gulp'; | ||||
| import browserify from 'browserify'; | ||||
| import source from 'vinyl-source-stream'; | ||||
| import buffer from 'vinyl-buffer'; | ||||
| import uglify from 'gulp-uglify'; | ||||
| import sourcemaps from 'gulp-sourcemaps'; | ||||
| import babel from 'babelify'; | ||||
|  | ||||
| gulp.task('browserify', function () { | ||||
|   let bundler = browserify({ | ||||
|     entries: './website/common/browserify.js', | ||||
|     debug: true, | ||||
|     transform: [[babel, { compact: false }]], | ||||
|   }); | ||||
|  | ||||
|   return bundler.bundle() | ||||
|     .pipe(source('habitrpg-shared.js')) | ||||
|     .pipe(buffer()) | ||||
|     .pipe(sourcemaps.init({loadMaps: true})) | ||||
|     .pipe(uglify()) | ||||
|     .on('error', function (err) { | ||||
|       console.error(err); | ||||
|       this.emit('end'); | ||||
|     }) | ||||
|     .pipe(sourcemaps.write('./')) | ||||
|     .pipe(gulp.dest('./website/client-old/js/')); | ||||
| }); | ||||
|  | ||||
| gulp.task('browserify:watch', () => { | ||||
|   gulp.watch('./website/common/script/**/*.js', ['browserify']); | ||||
| }); | ||||
| @@ -8,10 +8,10 @@ const BOOSTRAP_NEW_CONFIG_PATH = 'website/client/assets/scss/bootstrap_config.sc | ||||
| const BOOTSTRAP_ORIGINAL_CONFIG_PATH = 'node_modules/bootstrap/scss/_custom.scss'; | ||||
|  | ||||
| // https://stackoverflow.com/a/14387791/969528 | ||||
| function copyFile(source, target, cb) { | ||||
| function copyFile (source, target, cb) { | ||||
|   let cbCalled = false; | ||||
|  | ||||
|   function done(err) { | ||||
|   function done (err) { | ||||
|     if (!cbCalled) { | ||||
|       cb(err); | ||||
|       cbCalled = true; | ||||
| @@ -33,4 +33,4 @@ gulp.task('bootstrap', (done) => { | ||||
|     BOOTSTRAP_ORIGINAL_CONFIG_PATH, | ||||
|     done, | ||||
|   ); | ||||
| }); | ||||
| }); | ||||
|   | ||||
| @@ -1,14 +1,10 @@ | ||||
| import gulp from 'gulp'; | ||||
| import runSequence from 'run-sequence'; | ||||
| import babel from 'gulp-babel'; | ||||
| import webpackProductionBuild from '../webpack/build'; | ||||
| require('gulp-grunt')(gulp); | ||||
|  | ||||
| gulp.task('build', () => { | ||||
|   if (process.env.NODE_ENV === 'production') { | ||||
|   if (process.env.NODE_ENV === 'production') { // eslint-disable-line no-process-env | ||||
|     gulp.start('build:prod'); | ||||
|   } else { | ||||
|     gulp.start('build:dev'); | ||||
|   } | ||||
| }); | ||||
|  | ||||
| @@ -30,27 +26,12 @@ gulp.task('build:server', ['build:src', 'build:common']); | ||||
| gulp.task('build:client', ['bootstrap'], (done) => { | ||||
|   webpackProductionBuild((err, output) => { | ||||
|     if (err) return done(err); | ||||
|     console.log(output); | ||||
|     console.log(output); // eslint-disable-line no-console | ||||
|   }); | ||||
| }); | ||||
|  | ||||
| gulp.task('build:dev', ['browserify', 'prepare:staticNewStuff'], (done) => { | ||||
|   gulp.start('grunt-build:dev', done); | ||||
| }); | ||||
|  | ||||
| gulp.task('build:dev:watch', ['build:dev'], () => { | ||||
|   gulp.watch(['website/client-old/**/*.styl', 'website/common/script/*']); | ||||
| }); | ||||
|  | ||||
| gulp.task('build:prod', [ | ||||
|   'browserify',  | ||||
|   'build:server',  | ||||
|   'prepare:staticNewStuff',  | ||||
|   'build:server', | ||||
|   'build:client', | ||||
| ], (done) => { | ||||
|   runSequence( | ||||
|     'grunt-build:prod', | ||||
|     'apidoc', | ||||
|     done | ||||
|   ); | ||||
| }); | ||||
|   'apidoc', | ||||
| ]); | ||||
|   | ||||
| @@ -7,10 +7,11 @@ import gulp     from 'gulp'; | ||||
|  | ||||
| // Add additional properties to the repl's context | ||||
| let improveRepl = (context) => { | ||||
|  | ||||
|   // Let "exit" and "quit" terminate the console | ||||
|   ['exit', 'quit'].forEach((term) => { | ||||
|     Object.defineProperty(context, term, { get () { process.exit(); }}); | ||||
|     Object.defineProperty(context, term, { get () { | ||||
|       process.exit(); | ||||
|     }}); | ||||
|   }); | ||||
|  | ||||
|   // "clear" clears the screen | ||||
| @@ -18,12 +19,12 @@ let improveRepl = (context) => { | ||||
|     process.stdout.write('\u001B[2J\u001B[0;0f'); | ||||
|   }}); | ||||
|  | ||||
|   context.Challenge = require('../website/server/models/challenge').model; | ||||
|   context.Group     = require('../website/server/models/group').model; | ||||
|   context.User      = require('../website/server/models/user').model; | ||||
|   context.Challenge = require('../website/server/models/challenge').model; // eslint-disable-line global-require | ||||
|   context.Group     = require('../website/server/models/group').model; // eslint-disable-line global-require | ||||
|   context.User      = require('../website/server/models/user').model; // eslint-disable-line global-require | ||||
|  | ||||
|   var isProd = nconf.get('NODE_ENV') === 'production'; | ||||
|   var mongooseOptions = !isProd ? {} : { | ||||
|   const isProd = nconf.get('NODE_ENV') === 'production'; | ||||
|   const mongooseOptions = !isProd ? {} : { | ||||
|     replset: { socketOptions: { keepAlive: 1, connectTimeoutMS: 30000 } }, | ||||
|     server: { socketOptions: { keepAlive: 1, connectTimeoutMS: 30000 } }, | ||||
|   }; | ||||
| @@ -31,16 +32,15 @@ let improveRepl = (context) => { | ||||
|     mongoose.connect( | ||||
|       nconf.get('NODE_DB_URI'), | ||||
|       mongooseOptions, | ||||
|       function (err) { | ||||
|       (err) => { | ||||
|         if (err) throw err; | ||||
|         logger.info('Connected with Mongoose'); | ||||
|       } | ||||
|     ) | ||||
|   ); | ||||
|  | ||||
| }; | ||||
|  | ||||
| gulp.task('console', (cb) => { | ||||
| gulp.task('console', () => { | ||||
|   improveRepl(repl.start({ | ||||
|     prompt: 'Habitica > ', | ||||
|   }).context); | ||||
|   | ||||
| @@ -1,10 +0,0 @@ | ||||
| import gulp from 'gulp'; | ||||
| import jade from 'jade'; | ||||
| import {writeFileSync} from 'fs'; | ||||
|  | ||||
| gulp.task('prepare:staticNewStuff', () => { | ||||
|   writeFileSync( | ||||
|     './website/client-old/new-stuff.html', | ||||
|     jade.compileFile('./website/views/shared/new-stuff.jade')() | ||||
|   ); | ||||
| }); | ||||
| @@ -10,82 +10,39 @@ import {each} from 'lodash'; | ||||
|  | ||||
| // https://github.com/Ensighten/grunt-spritesmith/issues/67#issuecomment-34786248 | ||||
| const MAX_SPRITESHEET_SIZE = 1024 * 1024 * 3; | ||||
| const DIST_PATH = 'website/assets/sprites/dist/'; | ||||
|  | ||||
| const IMG_DIST_PATH_NEW_CLIENT = 'website/static/sprites/'; | ||||
| const CSS_DIST_PATH_NEW_CLIENT = 'website/client/assets/css/sprites/'; | ||||
| const IMG_DIST_PATH = 'website/static/sprites/'; | ||||
| const CSS_DIST_PATH = 'website/client/assets/css/sprites/'; | ||||
|  | ||||
| gulp.task('sprites:compile', ['sprites:clean', 'sprites:main', 'sprites:largeSprites', 'sprites:checkCompiledDimensions']); | ||||
| function checkForSpecialTreatment (name) { | ||||
|   let regex = /^hair|skin|beard|mustach|shirt|flower|^headAccessory_special_\w+Ears|^eyewear_special_\w+TopFrame/; | ||||
|   return name.match(regex) || name === 'head_0'; | ||||
| } | ||||
|  | ||||
| gulp.task('sprites:main', () => { | ||||
|   let mainSrc = sync('website/assets/sprites/spritesmith/**/*.png'); | ||||
|   return createSpritesStream('main', mainSrc); | ||||
| }); | ||||
| function calculateImgDimensions (img, addPadding) { | ||||
|   let dims = sizeOf(img); | ||||
|  | ||||
| gulp.task('sprites:largeSprites', () => { | ||||
|   let largeSrc = sync('website/assets/sprites/spritesmith_large/**/*.png'); | ||||
|   return createSpritesStream('largeSprites', largeSrc); | ||||
| }); | ||||
|  | ||||
| gulp.task('sprites:clean', (done) => { | ||||
|   clean(`{${DIST_PATH}spritesmith*,${IMG_DIST_PATH_NEW_CLIENT}spritesmith*,${CSS_DIST_PATH_NEW_CLIENT}spritesmith*}`, done); | ||||
| }); | ||||
|  | ||||
| gulp.task('sprites:checkCompiledDimensions', ['sprites:main', 'sprites:largeSprites'], () => { | ||||
|   console.log('Verifiying that images do not exceed max dimensions'); | ||||
|  | ||||
|   let numberOfSheetsThatAreTooBig = 0; | ||||
|  | ||||
|   let distSpritesheets = sync(`${DIST_PATH}*.png`); | ||||
|  | ||||
|   each(distSpritesheets, (img, index) => { | ||||
|     let spriteSize = calculateImgDimensions(img); | ||||
|  | ||||
|     if (spriteSize > MAX_SPRITESHEET_SIZE) { | ||||
|       numberOfSheetsThatAreTooBig++; | ||||
|       let name = basename(img, '.png'); | ||||
|       console.error(`WARNING: ${name} might be too big - ${spriteSize} > ${MAX_SPRITESHEET_SIZE}`); | ||||
|     } | ||||
|   }); | ||||
|  | ||||
|   if (numberOfSheetsThatAreTooBig > 0) { | ||||
|     console.error(`${numberOfSheetsThatAreTooBig} sheets might too big for mobile Safari to be able to handle them, but there is a margin of error in these calculations so it is probably okay. Mention this to an admin so they can test a staging site on mobile Safari after your PR is merged.`); // https://github.com/HabitRPG/habitica/pull/6683#issuecomment-185462180 | ||||
|   } else { | ||||
|     console.log('All images are within the correct dimensions'); | ||||
|   let requiresSpecialTreatment = checkForSpecialTreatment(img); | ||||
|   if (requiresSpecialTreatment) { | ||||
|     let newWidth = dims.width < 90 ? 90 : dims.width; | ||||
|     let newHeight = dims.height < 90 ? 90 : dims.height; | ||||
|     dims = { | ||||
|       width: newWidth, | ||||
|       height: newHeight, | ||||
|     }; | ||||
|   } | ||||
| }); | ||||
|  | ||||
| function createSpritesStream (name, src) { | ||||
|   let spritesheetSliceIndicies = calculateSpritesheetsSrcIndicies(src); | ||||
|   let stream = mergeStream(); | ||||
|   let padding = 0; | ||||
|  | ||||
|   each(spritesheetSliceIndicies, (start, index) => { | ||||
|     let slicedSrc = src.slice(start, spritesheetSliceIndicies[index + 1]); | ||||
|   if (addPadding) { | ||||
|     padding = dims.width * 8 + dims.height * 8; | ||||
|   } | ||||
|  | ||||
|     let spriteData = gulp.src(slicedSrc) | ||||
|       .pipe(spritesmith({ | ||||
|         imgName: `spritesmith-${name}-${index}.png`, | ||||
|         cssName: `spritesmith-${name}-${index}.css`, | ||||
|         algorithm: 'binary-tree', | ||||
|         padding: 1, | ||||
|         cssTemplate: 'website/assets/sprites/css/css.template.handlebars', | ||||
|         cssVarMap: cssVarMap, | ||||
|       })); | ||||
|   if (!dims.width || !dims.height) console.error('MISSING DIMENSIONS:', dims); // eslint-disable-line no-console | ||||
|  | ||||
|     let imgStream = spriteData.img | ||||
|       .pipe(imagemin()) | ||||
|       .pipe(gulp.dest(IMG_DIST_PATH_NEW_CLIENT)) | ||||
|       .pipe(gulp.dest(DIST_PATH)); | ||||
|   let totalPixelSize = dims.width * dims.height + padding; | ||||
|  | ||||
|     let cssStream = spriteData.css | ||||
|       .pipe(gulp.dest(CSS_DIST_PATH_NEW_CLIENT)) | ||||
|       .pipe(gulp.dest(DIST_PATH)); | ||||
|  | ||||
|     stream.add(imgStream); | ||||
|     stream.add(cssStream); | ||||
|   }); | ||||
|  | ||||
|   return stream; | ||||
|   return totalPixelSize; | ||||
| } | ||||
|  | ||||
| function calculateSpritesheetsSrcIndicies (src) { | ||||
| @@ -105,37 +62,6 @@ function calculateSpritesheetsSrcIndicies (src) { | ||||
|   return slices; | ||||
| } | ||||
|  | ||||
| function calculateImgDimensions (img, addPadding) { | ||||
|   let dims = sizeOf(img); | ||||
|  | ||||
|   let requiresSpecialTreatment = checkForSpecialTreatment(img); | ||||
|   if (requiresSpecialTreatment) { | ||||
|     let newWidth = dims.width < 90 ? 90 : dims.width; | ||||
|     let newHeight = dims.height < 90 ? 90 : dims.height; | ||||
|     dims = { | ||||
|       width: newWidth, | ||||
|       height: newHeight, | ||||
|     }; | ||||
|   } | ||||
|  | ||||
|   let padding = 0; | ||||
|  | ||||
|   if (addPadding) { | ||||
|     padding = (dims.width * 8) + (dims.height * 8); | ||||
|   } | ||||
|  | ||||
|   if (!dims.width || !dims.height) console.error('MISSING DIMENSIONS:', dims); | ||||
|  | ||||
|   let totalPixelSize = (dims.width * dims.height) + padding; | ||||
|  | ||||
|   return totalPixelSize; | ||||
| } | ||||
|  | ||||
| function checkForSpecialTreatment (name) { | ||||
|   let regex = /^hair|skin|beard|mustach|shirt|flower|^headAccessory_special_\w+Ears|^eyewear_special_\w+TopFrame/; | ||||
|   return name.match(regex) || name === 'head_0'; | ||||
| } | ||||
|  | ||||
| function cssVarMap (sprite) { | ||||
|   // For hair, skins, beards, etc. we want to output a '.customize-options.WHATEVER' class, which works as a | ||||
|   // 60x60 image pointing at the proper part of the 90x90 sprite. | ||||
| @@ -144,18 +70,93 @@ function cssVarMap (sprite) { | ||||
|   if (requiresSpecialTreatment) { | ||||
|     sprite.custom = { | ||||
|       px: { | ||||
|         offset_x: `-${ sprite.x + 25 }px`, | ||||
|         offset_y: `-${ sprite.y + 15 }px`, | ||||
|         offsetX: `-${ sprite.x + 25 }px`, | ||||
|         offsetY: `-${ sprite.y + 15 }px`, | ||||
|         width: '60px', | ||||
|         height: '60px', | ||||
|       }, | ||||
|     }; | ||||
|   } | ||||
|   if (~sprite.name.indexOf('shirt')) | ||||
|     sprite.custom.px.offset_y = `-${ sprite.y + 30 }px`; // even more for shirts | ||||
|   if (~sprite.name.indexOf('hair_base')) { | ||||
|     let styleArray = sprite.name.split('_').slice(2,3); | ||||
|   if (sprite.name.indexOf('shirt') !== -1) | ||||
|     sprite.custom.px.offsetY = `-${ sprite.y + 35 }px`; // even more for shirts | ||||
|   if (sprite.name.indexOf('hair_base') !== -1) { | ||||
|     let styleArray = sprite.name.split('_').slice(2, 3); | ||||
|     if (Number(styleArray[0]) > 14) | ||||
|       sprite.custom.px.offset_y = `-${ sprite.y }px`; // don't crop updos | ||||
|       sprite.custom.px.offsetY = `-${ sprite.y }px`; // don't crop updos | ||||
|   } | ||||
| } | ||||
|  | ||||
| function createSpritesStream (name, src) { | ||||
|   let spritesheetSliceIndicies = calculateSpritesheetsSrcIndicies(src); | ||||
|   let stream = mergeStream(); | ||||
|  | ||||
|   each(spritesheetSliceIndicies, (start, index) => { | ||||
|     let slicedSrc = src.slice(start, spritesheetSliceIndicies[index + 1]); | ||||
|  | ||||
|     let spriteData = gulp.src(slicedSrc) | ||||
|       .pipe(spritesmith({ | ||||
|         imgName: `spritesmith-${name}-${index}.png`, | ||||
|         cssName: `spritesmith-${name}-${index}.css`, | ||||
|         algorithm: 'binary-tree', | ||||
|         padding: 1, | ||||
|         cssTemplate: 'website/raw_sprites/css/css.template.handlebars', | ||||
|         cssVarMap, | ||||
|       })); | ||||
|  | ||||
|     let imgStream = spriteData.img | ||||
|       .pipe(imagemin()) | ||||
|       .pipe(gulp.dest(IMG_DIST_PATH)); | ||||
|  | ||||
|     let cssStream = spriteData.css | ||||
|       .pipe(gulp.dest(CSS_DIST_PATH)); | ||||
|  | ||||
|     stream.add(imgStream); | ||||
|     stream.add(cssStream); | ||||
|   }); | ||||
|  | ||||
|   return stream; | ||||
| } | ||||
|  | ||||
| gulp.task('sprites:compile', ['sprites:clean', 'sprites:main', 'sprites:largeSprites', 'sprites:checkCompiledDimensions']); | ||||
|  | ||||
| gulp.task('sprites:main', () => { | ||||
|   let mainSrc = sync('website/raw_sprites/spritesmith/**/*.png'); | ||||
|   return createSpritesStream('main', mainSrc); | ||||
| }); | ||||
|  | ||||
| gulp.task('sprites:largeSprites', () => { | ||||
|   let largeSrc = sync('website/raw_sprites/spritesmith_large/**/*.png'); | ||||
|   return createSpritesStream('largeSprites', largeSrc); | ||||
| }); | ||||
|  | ||||
| gulp.task('sprites:clean', (done) => { | ||||
|   clean(`${IMG_DIST_PATH}spritesmith*,${CSS_DIST_PATH}spritesmith*}`, done); | ||||
| }); | ||||
|  | ||||
| gulp.task('sprites:checkCompiledDimensions', ['sprites:main', 'sprites:largeSprites'], () => { | ||||
|   console.log('Verifiying that images do not exceed max dimensions'); // eslint-disable-line no-console | ||||
|  | ||||
|   let numberOfSheetsThatAreTooBig = 0; | ||||
|  | ||||
|   let distSpritesheets = sync(`${IMG_DIST_PATH}*.png`); | ||||
|  | ||||
|   each(distSpritesheets, (img) => { | ||||
|     let spriteSize = calculateImgDimensions(img); | ||||
|  | ||||
|     if (spriteSize > MAX_SPRITESHEET_SIZE) { | ||||
|       numberOfSheetsThatAreTooBig++; | ||||
|       let name = basename(img, '.png'); | ||||
|       console.error(`WARNING: ${name} might be too big - ${spriteSize} > ${MAX_SPRITESHEET_SIZE}`); // eslint-disable-line no-console | ||||
|     } | ||||
|   }); | ||||
|  | ||||
|   if (numberOfSheetsThatAreTooBig > 0) { | ||||
|     // https://github.com/HabitRPG/habitica/pull/6683#issuecomment-185462180 | ||||
|     console.error( // eslint-disable-line no-console | ||||
|       `${numberOfSheetsThatAreTooBig} sheets might too big for mobile Safari to be able to handle | ||||
|       them, but there is a margin of error in these calculations so it is probably okay. Mention | ||||
|       this to an admin so they can test a staging site on mobile Safari after your PR is merged.`); | ||||
|   } else { | ||||
|     console.log('All images are within the correct dimensions'); // eslint-disable-line no-console | ||||
|   } | ||||
| }); | ||||
|   | ||||
| @@ -3,8 +3,6 @@ import nodemon from 'gulp-nodemon'; | ||||
|  | ||||
| let pkg = require('../package.json'); | ||||
|  | ||||
| gulp.task('run:dev', ['nodemon', 'build:dev:watch']); | ||||
|  | ||||
| gulp.task('nodemon', () => { | ||||
|   nodemon({ | ||||
|     script: pkg.main, | ||||
|   | ||||
| @@ -1,21 +1,12 @@ | ||||
| import { | ||||
|   pipe, | ||||
|   awaitPort, | ||||
|   kill, | ||||
|   runMochaTests, | ||||
| }  from './taskHelper'; | ||||
| import { server as karma }        from 'karma'; | ||||
| import mongoose                   from 'mongoose'; | ||||
| import { exec }                   from 'child_process'; | ||||
| import psTree                     from 'ps-tree'; | ||||
| import gulp                       from 'gulp'; | ||||
| import Bluebird                   from 'bluebird'; | ||||
| import runSequence                from 'run-sequence'; | ||||
| import os                         from 'os'; | ||||
| import nconf                      from 'nconf'; | ||||
| import fs from 'fs'; | ||||
|  | ||||
| const i18n = require('../website/server/libs/i18n'); | ||||
|  | ||||
| // TODO rewrite | ||||
|  | ||||
| @@ -24,25 +15,23 @@ let server; | ||||
|  | ||||
| const TEST_DB_URI       = nconf.get('TEST_DB_URI'); | ||||
|  | ||||
| const API_V3_TEST_COMMAND = 'npm run test:api-v3'; | ||||
| const SANITY_TEST_COMMAND = 'npm run test:sanity'; | ||||
| const COMMON_TEST_COMMAND = 'npm run test:common'; | ||||
| const CONTENT_TEST_COMMAND = 'npm run test:content'; | ||||
| const CONTENT_OPTIONS = {maxBuffer: 1024 * 500}; | ||||
| const KARMA_TEST_COMMAND = 'npm run test:karma'; | ||||
|  | ||||
| /* Helper methods for reporting test summary */ | ||||
| let testResults = []; | ||||
| let testCount = (stdout, regexp) => { | ||||
|   let match = stdout.match(regexp); | ||||
|   return parseInt(match && match[1] || 0); | ||||
|   return parseInt(match && match[1] || 0, 10); | ||||
| }; | ||||
|  | ||||
| let testBin = (string, additionalEnvVariables = '') => { | ||||
|   if (os.platform() === 'win32') { | ||||
|     if (additionalEnvVariables != '') { | ||||
|     if (additionalEnvVariables !== '') { | ||||
|       additionalEnvVariables = additionalEnvVariables.split(' ').join('&&set '); | ||||
|       additionalEnvVariables = 'set ' + additionalEnvVariables + '&&'; | ||||
|       additionalEnvVariables = `set ${additionalEnvVariables}&&`; | ||||
|     } | ||||
|     return `set NODE_ENV=test&&${additionalEnvVariables}${string}`; | ||||
|   } else { | ||||
| @@ -50,9 +39,9 @@ let testBin = (string, additionalEnvVariables = '') => { | ||||
|   } | ||||
| }; | ||||
|  | ||||
| gulp.task('test:nodemon', (done) => { | ||||
|   process.env.PORT = TEST_SERVER_PORT; | ||||
|   process.env.NODE_DB_URI = TEST_DB_URI; | ||||
| gulp.task('test:nodemon', () => { | ||||
|   process.env.PORT = TEST_SERVER_PORT; // eslint-disable-line no-process-env | ||||
|   process.env.NODE_DB_URI = TEST_DB_URI; // eslint-disable-line no-process-env | ||||
|  | ||||
|   runSequence('nodemon'); | ||||
| }); | ||||
| @@ -69,37 +58,27 @@ gulp.task('test:prepare:mongo', (cb) => { | ||||
| gulp.task('test:prepare:server', ['test:prepare:mongo'], () => { | ||||
|   if (!server) { | ||||
|     server = exec(testBin('node ./website/server/index.js', `NODE_DB_URI=${TEST_DB_URI} PORT=${TEST_SERVER_PORT}`), (error, stdout, stderr) => { | ||||
|       if (error) { throw `Problem with the server: ${error}`; } | ||||
|       if (stderr) { console.error(stderr); } | ||||
|       if (error) { | ||||
|         throw new Error(`Problem with the server: ${error}`); | ||||
|       } | ||||
|       if (stderr) { | ||||
|         console.error(stderr); // eslint-disable-line no-console | ||||
|       } | ||||
|     }); | ||||
|   } | ||||
| }); | ||||
|  | ||||
| gulp.task('test:prepare:translations', (cb) => { | ||||
|   fs.writeFile( | ||||
|     'test/client-old/spec/mocks/translations.js', | ||||
|      `if(!window.env) window.env = {}; | ||||
| window.env.translations = ${JSON.stringify(i18n.translations['en'])};`, cb); | ||||
|  | ||||
| }); | ||||
|  | ||||
| gulp.task('test:prepare:build', ['build', 'test:prepare:translations']); | ||||
| // exec(testBin('grunt build:test'), cb); | ||||
|  | ||||
| gulp.task('test:prepare:webdriver', (cb) => { | ||||
|   exec('npm run test:prepare:webdriver', cb); | ||||
| }); | ||||
| gulp.task('test:prepare:build', ['build']); | ||||
|  | ||||
| gulp.task('test:prepare', [ | ||||
|   'test:prepare:build', | ||||
|   'test:prepare:mongo', | ||||
|   'test:prepare:webdriver', | ||||
| ]); | ||||
|  | ||||
| gulp.task('test:sanity', (cb) => { | ||||
|   let runner = exec( | ||||
|     testBin(SANITY_TEST_COMMAND), | ||||
|     (err, stdout, stderr) => { | ||||
|     (err) => { | ||||
|       if (err) { | ||||
|         process.exit(1); | ||||
|       } | ||||
| @@ -112,7 +91,7 @@ gulp.task('test:sanity', (cb) => { | ||||
| gulp.task('test:common', ['test:prepare:build'], (cb) => { | ||||
|   let runner = exec( | ||||
|     testBin(COMMON_TEST_COMMAND), | ||||
|     (err, stdout, stderr) => { | ||||
|     (err) => { | ||||
|       if (err) { | ||||
|         process.exit(1); | ||||
|       } | ||||
| @@ -133,7 +112,7 @@ gulp.task('test:common:watch', ['test:common:clean'], () => { | ||||
| gulp.task('test:common:safe', ['test:prepare:build'], (cb) => { | ||||
|   let runner = exec( | ||||
|     testBin(COMMON_TEST_COMMAND), | ||||
|     (err, stdout, stderr) => { | ||||
|     (err, stdout) => { // eslint-disable-line handle-callback-err | ||||
|       testResults.push({ | ||||
|         suite: 'Common Specs\t', | ||||
|         pass: testCount(stdout, /(\d+) passing/), | ||||
| @@ -150,7 +129,7 @@ gulp.task('test:content', ['test:prepare:build'], (cb) => { | ||||
|   let runner = exec( | ||||
|     testBin(CONTENT_TEST_COMMAND), | ||||
|     CONTENT_OPTIONS, | ||||
|     (err, stdout, stderr) => { | ||||
|     (err) => { | ||||
|       if (err) { | ||||
|         process.exit(1); | ||||
|       } | ||||
| @@ -172,7 +151,7 @@ gulp.task('test:content:safe', ['test:prepare:build'], (cb) => { | ||||
|   let runner = exec( | ||||
|     testBin(CONTENT_TEST_COMMAND), | ||||
|     CONTENT_OPTIONS, | ||||
|     (err, stdout, stderr) => { | ||||
|     (err, stdout) => {  // eslint-disable-line handle-callback-err | ||||
|       testResults.push({ | ||||
|         suite: 'Content Specs\t', | ||||
|         pass: testCount(stdout, /(\d+) passing/), | ||||
| @@ -185,103 +164,10 @@ gulp.task('test:content:safe', ['test:prepare:build'], (cb) => { | ||||
|   pipe(runner); | ||||
| }); | ||||
|  | ||||
| gulp.task('test:karma', ['test:prepare:build'], (cb) => { | ||||
|   let runner = exec( | ||||
|     testBin(KARMA_TEST_COMMAND), | ||||
|     (err, stdout) => { | ||||
|       if (err) { | ||||
|         process.exit(1); | ||||
|       } | ||||
|       cb(); | ||||
|     } | ||||
|   ); | ||||
|   pipe(runner); | ||||
| }); | ||||
|  | ||||
| gulp.task('test:karma:watch', ['test:prepare:build'], (cb) => { | ||||
|   let runner = exec( | ||||
|     testBin(`${KARMA_TEST_COMMAND}:watch`), | ||||
|     (err, stdout) => { | ||||
|       cb(err); | ||||
|     } | ||||
|   ); | ||||
|   pipe(runner); | ||||
| }); | ||||
|  | ||||
| gulp.task('test:karma:safe', ['test:prepare:build'], (cb) => { | ||||
|   let runner = exec( | ||||
|     testBin(KARMA_TEST_COMMAND), | ||||
|     (err, stdout) => { | ||||
|       testResults.push({ | ||||
|         suite: 'Karma Specs\t', | ||||
|         pass: testCount(stdout, /(\d+) tests? completed/), | ||||
|         fail: testCount(stdout, /(\d+) tests? failed/), | ||||
|         pend: testCount(stdout, /(\d+) tests? skipped/), | ||||
|       }); | ||||
|       cb(); | ||||
|     } | ||||
|   ); | ||||
|   pipe(runner); | ||||
| }); | ||||
|  | ||||
| gulp.task('test:e2e', ['test:prepare', 'test:prepare:server'], (cb) => { | ||||
|   let support = [ | ||||
|     'Xvfb :99 -screen 0 1024x768x24 -extension RANDR', | ||||
|     testBin('npm run test:e2e:webdriver', 'DISPLAY=:99'), | ||||
|   ].map(exec); | ||||
|   support.push(server); | ||||
|  | ||||
|   Bluebird.all([ | ||||
|     awaitPort(TEST_SERVER_PORT), | ||||
|     awaitPort(4444), | ||||
|   ]).then(() => { | ||||
|     let runner = exec( | ||||
|       'npm run test:e2e', | ||||
|       (err, stdout, stderr) => { | ||||
|         support.forEach(kill); | ||||
|         if (err) { | ||||
|           process.exit(1); | ||||
|         } | ||||
|         cb(); | ||||
|       } | ||||
|     ); | ||||
|     pipe(runner); | ||||
|   }); | ||||
| }); | ||||
|  | ||||
| gulp.task('test:e2e:safe', ['test:prepare', 'test:prepare:server'], (cb) => { | ||||
|   let support = [ | ||||
|     'Xvfb :99 -screen 0 1024x768x24 -extension RANDR', | ||||
|     'npm run test:e2e:webdriver', | ||||
|   ].map(exec); | ||||
|  | ||||
|   Bluebird.all([ | ||||
|     awaitPort(TEST_SERVER_PORT), | ||||
|     awaitPort(4444), | ||||
|   ]).then(() => { | ||||
|     let runner = exec( | ||||
|       'npm run test:e2e', | ||||
|       (err, stdout, stderr) => { | ||||
|         let match = stdout.match(/(\d+) tests?.*(\d) failures?/); | ||||
|  | ||||
|         testResults.push({ | ||||
|           suite: 'End-to-End Specs\t', | ||||
|           pass: testCount(stdout, /(\d+) passing/), | ||||
|           fail: testCount(stdout, /(\d+) failing/), | ||||
|           pend: testCount(stdout, /(\d+) pending/), | ||||
|         }); | ||||
|         support.forEach(kill); | ||||
|         cb(); | ||||
|       } | ||||
|     ); | ||||
|     pipe(runner); | ||||
|   }); | ||||
| }); | ||||
|  | ||||
| gulp.task('test:api-v3:unit', (done) => { | ||||
|   let runner = exec( | ||||
|     testBin('node_modules/.bin/istanbul cover --dir coverage/api-v3-unit --report lcovonly node_modules/mocha/bin/_mocha -- test/api/v3/unit --recursive --require ./test/helpers/start-server'), | ||||
|     (err, stdout, stderr) => { | ||||
|     (err) => { | ||||
|       if (err) { | ||||
|         process.exit(1); | ||||
|       } | ||||
| @@ -300,7 +186,7 @@ gulp.task('test:api-v3:integration', (done) => { | ||||
|   let runner = exec( | ||||
|     testBin('node_modules/.bin/istanbul cover --dir coverage/api-v3-integration --report lcovonly node_modules/mocha/bin/_mocha -- test/api/v3/integration --recursive --require ./test/helpers/start-server'), | ||||
|     {maxBuffer: 500 * 1024}, | ||||
|     (err, stdout, stderr) => { | ||||
|     (err) => { | ||||
|       if (err) { | ||||
|         process.exit(1); | ||||
|       } | ||||
| @@ -320,7 +206,7 @@ gulp.task('test:api-v3:integration:separate-server', (done) => { | ||||
|   let runner = exec( | ||||
|     testBin('mocha test/api/v3/integration --recursive --require ./test/helpers/start-server', 'LOAD_SERVER=0'), | ||||
|     {maxBuffer: 500 * 1024}, | ||||
|     (err, stdout, stderr) => done(err) | ||||
|     (err) => done(err) | ||||
|   ); | ||||
|  | ||||
|   pipe(runner); | ||||
| @@ -331,7 +217,6 @@ gulp.task('test', (done) => { | ||||
|     'test:sanity', | ||||
|     'test:content', | ||||
|     'test:common', | ||||
|     'test:karma', | ||||
|     'test:api-v3:unit', | ||||
|     'test:api-v3:integration', | ||||
|     done | ||||
|   | ||||
| @@ -1,6 +1,5 @@ | ||||
| import fs       from 'fs'; | ||||
| import _        from 'lodash'; | ||||
| import nconf    from 'nconf'; | ||||
| import gulp     from 'gulp'; | ||||
| import { postToSlack, conf } from './taskHelper'; | ||||
|  | ||||
| @@ -12,8 +11,82 @@ const SLACK_CONFIG = { | ||||
|  | ||||
| const LOCALES = './website/common/locales/'; | ||||
| const ENGLISH_LOCALE = `${LOCALES}en/`; | ||||
|  | ||||
|  | ||||
| function getArrayOfLanguages () { | ||||
|   let languages = fs.readdirSync(LOCALES); | ||||
|   languages.shift(); // Remove README.md from array of languages | ||||
|  | ||||
|   return languages; | ||||
| } | ||||
|  | ||||
| const ALL_LANGUAGES = getArrayOfLanguages(); | ||||
|  | ||||
| function stripOutNonJsonFiles (collection) { | ||||
|   let onlyJson = _.filter(collection, (file) => { | ||||
|     return file.match(/[a-zA-Z]*\.json/); | ||||
|   }); | ||||
|  | ||||
|   return onlyJson; | ||||
| } | ||||
|  | ||||
| function eachTranslationFile (languages, cb) { | ||||
|   let jsonFiles = stripOutNonJsonFiles(fs.readdirSync(ENGLISH_LOCALE)); | ||||
|  | ||||
|   _.each(languages, (lang) => { | ||||
|     _.each(jsonFiles, (filename) => { | ||||
|       let parsedTranslationFile; | ||||
|       try { | ||||
|         const translationFile = fs.readFileSync(`${LOCALES}${lang}/${filename}`); | ||||
|         parsedTranslationFile = JSON.parse(translationFile); | ||||
|       } catch (err) { | ||||
|         return cb(err); | ||||
|       } | ||||
|  | ||||
|       let englishFile = fs.readFileSync(ENGLISH_LOCALE + filename); | ||||
|       let parsedEnglishFile = JSON.parse(englishFile); | ||||
|  | ||||
|       cb(null, lang, filename, parsedEnglishFile, parsedTranslationFile); | ||||
|     }); | ||||
|   }); | ||||
| } | ||||
|  | ||||
| function eachTranslationString (languages, cb) { | ||||
|   eachTranslationFile(languages, (error, language, filename, englishJSON, translationJSON) => { | ||||
|     if (error) return; | ||||
|     _.each(englishJSON, (string, key) => { | ||||
|       const translationString = translationJSON[key]; | ||||
|       cb(language, filename, key, string, translationString); | ||||
|     }); | ||||
|   }); | ||||
| } | ||||
|  | ||||
| function formatMessageForPosting (msg, items) { | ||||
|   let body = `*Warning:* ${msg}`; | ||||
|   body += '\n\n```\n'; | ||||
|   body += items.join('\n'); | ||||
|   body += '\n```'; | ||||
|  | ||||
|   return body; | ||||
| } | ||||
|  | ||||
| function getStringsWith (json, interpolationRegex) { | ||||
|   let strings = {}; | ||||
|  | ||||
|   _.each(json, (fileName) => { | ||||
|     const rawFile = fs.readFileSync(ENGLISH_LOCALE + fileName); | ||||
|     const parsedJson = JSON.parse(rawFile); | ||||
|  | ||||
|     strings[fileName] = {}; | ||||
|     _.each(parsedJson, (value, key) => { | ||||
|       const match = value.match(interpolationRegex); | ||||
|       if (match) strings[fileName][key] = match; | ||||
|     }); | ||||
|   }); | ||||
|  | ||||
|   return strings; | ||||
| } | ||||
|  | ||||
| const malformedStringExceptions = { | ||||
|   messageDropFood: true, | ||||
|   armoireFood: true, | ||||
| @@ -23,7 +96,6 @@ const malformedStringExceptions = { | ||||
| gulp.task('transifex', ['transifex:missingFiles', 'transifex:missingStrings', 'transifex:malformedStrings']); | ||||
|  | ||||
| gulp.task('transifex:missingFiles', () => { | ||||
|  | ||||
|   let missingStrings = []; | ||||
|  | ||||
|   eachTranslationFile(ALL_LANGUAGES, (error) => { | ||||
| @@ -40,7 +112,6 @@ gulp.task('transifex:missingFiles', () => { | ||||
| }); | ||||
|  | ||||
| gulp.task('transifex:missingStrings', () => { | ||||
|  | ||||
|   let missingStrings = []; | ||||
|  | ||||
|   eachTranslationString(ALL_LANGUAGES, (language, filename, key, englishString, translationString) => { | ||||
| @@ -58,7 +129,6 @@ gulp.task('transifex:missingStrings', () => { | ||||
| }); | ||||
|  | ||||
| gulp.task('transifex:malformedStrings', () => { | ||||
|  | ||||
|   let jsonFiles = stripOutNonJsonFiles(fs.readdirSync(ENGLISH_LOCALE)); | ||||
|   let interpolationRegex = /<%= [a-zA-Z]* %>/g; | ||||
|   let stringsToLookFor = getStringsWith(jsonFiles, interpolationRegex); | ||||
| @@ -66,25 +136,23 @@ gulp.task('transifex:malformedStrings', () => { | ||||
|   let stringsWithMalformedInterpolations = []; | ||||
|   let stringsWithIncorrectNumberOfInterpolations = []; | ||||
|  | ||||
|   let count = 0; | ||||
|   _.each(ALL_LANGUAGES, function (lang) { | ||||
|  | ||||
|     _.each(stringsToLookFor, function (strings, file) { | ||||
|       let translationFile = fs.readFileSync(LOCALES + lang + '/' + file); | ||||
|   _.each(ALL_LANGUAGES, (lang) => { | ||||
|     _.each(stringsToLookFor, (strings, filename) => { | ||||
|       let translationFile = fs.readFileSync(`${LOCALES}${lang}/${filename}`); | ||||
|       let parsedTranslationFile = JSON.parse(translationFile); | ||||
|  | ||||
|       _.each(strings, function (value, key) { | ||||
|       _.each(strings, (value, key) => { // eslint-disable-line max-nested-callbacks | ||||
|         let translationString = parsedTranslationFile[key]; | ||||
|         if (!translationString) return; | ||||
|  | ||||
|         let englishOccurences = stringsToLookFor[file][key]; | ||||
|         let englishOccurences = stringsToLookFor[filename][key]; | ||||
|         let translationOccurences = translationString.match(interpolationRegex); | ||||
|  | ||||
|         if (!translationOccurences) { | ||||
|           let malformedString = `${lang} - ${file} - ${key} - ${translationString}`; | ||||
|           let malformedString = `${lang} - ${filename} - ${key} - ${translationString}`; | ||||
|           stringsWithMalformedInterpolations.push(malformedString); | ||||
|         } else if (englishOccurences.length !== translationOccurences.length && !malformedStringExceptions[key]) { | ||||
|           let missingInterpolationString = `${lang} - ${file} - ${key} - ${translationString}`; | ||||
|           let missingInterpolationString = `${lang} - ${filename} - ${key} - ${translationString}`; | ||||
|           stringsWithIncorrectNumberOfInterpolations.push(missingInterpolationString); | ||||
|         } | ||||
|       }); | ||||
| @@ -103,74 +171,3 @@ gulp.task('transifex:malformedStrings', () => { | ||||
|     postToSlack(formattedMessage, SLACK_CONFIG); | ||||
|   } | ||||
| }); | ||||
|  | ||||
| function getArrayOfLanguages () { | ||||
|   let languages = fs.readdirSync(LOCALES); | ||||
|   languages.shift(); // Remove README.md from array of languages | ||||
|  | ||||
|   return languages; | ||||
| } | ||||
|  | ||||
| function eachTranslationFile (languages, cb) { | ||||
|   let jsonFiles = stripOutNonJsonFiles(fs.readdirSync(ENGLISH_LOCALE)); | ||||
|  | ||||
|   _.each(languages, (lang) => { | ||||
|     _.each(jsonFiles, (filename) => { | ||||
|       try { | ||||
|         var translationFile = fs.readFileSync(LOCALES + lang + '/' + filename); | ||||
|         var parsedTranslationFile = JSON.parse(translationFile); | ||||
|       } catch (err) { | ||||
|         return cb(err); | ||||
|       } | ||||
|  | ||||
|       let englishFile = fs.readFileSync(ENGLISH_LOCALE + filename); | ||||
|       let parsedEnglishFile = JSON.parse(englishFile); | ||||
|  | ||||
|       cb(null, lang, filename, parsedEnglishFile, parsedTranslationFile); | ||||
|     }); | ||||
|   }); | ||||
| } | ||||
|  | ||||
| function eachTranslationString (languages, cb) { | ||||
|   eachTranslationFile(languages, (error, language, filename, englishJSON, translationJSON) => { | ||||
|     if (error) return; | ||||
|     _.each(englishJSON, (string, key) => { | ||||
|       var translationString = translationJSON[key]; | ||||
|       cb(language, filename, key, string, translationString); | ||||
|     }); | ||||
|   }); | ||||
| } | ||||
|  | ||||
| function formatMessageForPosting (msg, items) { | ||||
|   let body = `*Warning:* ${msg}`; | ||||
|   body += '\n\n```\n'; | ||||
|   body += items.join('\n'); | ||||
|   body += '\n```'; | ||||
|  | ||||
|   return body; | ||||
| } | ||||
|  | ||||
| function getStringsWith (json, interpolationRegex) { | ||||
|   var strings = {}; | ||||
|  | ||||
|   _.each(json, function (file_name) { | ||||
|     var raw_file = fs.readFileSync(ENGLISH_LOCALE + file_name); | ||||
|     var parsed_json = JSON.parse(raw_file); | ||||
|  | ||||
|     strings[file_name] = {}; | ||||
|     _.each(parsed_json, function (value, key) { | ||||
|       var match = value.match(interpolationRegex); | ||||
|       if (match) strings[file_name][key] = match; | ||||
|     }); | ||||
|   }); | ||||
|  | ||||
|   return strings; | ||||
| } | ||||
|  | ||||
| function stripOutNonJsonFiles (collection) { | ||||
|   let onlyJson = _.filter(collection, (file) => { | ||||
|     return file.match(/[a-zA-Z]*\.json/); | ||||
|   }); | ||||
|  | ||||
|   return onlyJson; | ||||
| } | ||||
|   | ||||
| @@ -12,7 +12,7 @@ import { resolve }                from 'path'; | ||||
|  * Get access to configruable values | ||||
|  */ | ||||
| nconf.argv().env().file({ file: 'config.json' }); | ||||
| export var conf = nconf; | ||||
| export const conf = nconf; | ||||
|  | ||||
| /* | ||||
|  * Kill a child process and any sub-children that process may have spawned. | ||||
| @@ -26,11 +26,12 @@ export function kill (proc) { | ||||
|         pids.forEach(kill); return; | ||||
|       } | ||||
|       try { | ||||
|         exec(/^win/.test(process.platform) | ||||
|           ? `taskkill /PID ${pid} /T /F` | ||||
|           : `kill -9 ${pid}`); | ||||
|         exec(/^win/.test(process.platform) ? | ||||
|           `taskkill /PID ${pid} /T /F` : | ||||
|           `kill -9 ${pid}`); | ||||
|       } catch (e) { | ||||
|         console.log(e); // eslint-disable-line no-console | ||||
|       } | ||||
|       catch (e) { console.log(e); } | ||||
|     }); | ||||
|   }; | ||||
|  | ||||
| @@ -44,21 +45,25 @@ export function kill (proc) { | ||||
|  * before failing. | ||||
|  */ | ||||
| export function awaitPort (port, max = 60) { | ||||
|   return new Bluebird((reject, resolve) => { | ||||
|     let socket, timeout, interval; | ||||
|   return new Bluebird((rej, res) => { | ||||
|     let socket; | ||||
|     let timeout; | ||||
|     let interval; | ||||
|  | ||||
|     timeout = setTimeout(() => { | ||||
|       clearInterval(interval); | ||||
|       reject(`Timed out after ${max} seconds`); | ||||
|       rej(`Timed out after ${max} seconds`); | ||||
|     }, max * 1000); | ||||
|  | ||||
|     interval = setInterval(() => { | ||||
|       socket = net.connect({port: port}, () => { | ||||
|       socket = net.connect({port}, () => { | ||||
|         clearInterval(interval); | ||||
|         clearTimeout(timeout); | ||||
|         socket.destroy(); | ||||
|         resolve(); | ||||
|       }).on('error', () => { socket.destroy; }); | ||||
|         res(); | ||||
|       }).on('error', () => { | ||||
|         socket.destroy(); | ||||
|       }); | ||||
|     }, 1000); | ||||
|   }); | ||||
| } | ||||
| @@ -67,8 +72,12 @@ export function awaitPort (port, max = 60) { | ||||
|  * Pipe the child's stdin and stderr to the parent process. | ||||
|  */ | ||||
| export function pipe (child) { | ||||
|   child.stdout.on('data', (data) => { process.stdout.write(data); }); | ||||
|   child.stderr.on('data', (data) => { process.stderr.write(data); }); | ||||
|   child.stdout.on('data', (data) => { | ||||
|     process.stdout.write(data); | ||||
|   }); | ||||
|   child.stderr.on('data', (data) => { | ||||
|     process.stderr.write(data); | ||||
|   }); | ||||
| } | ||||
|  | ||||
| /* | ||||
| @@ -78,8 +87,8 @@ export function postToSlack (msg, config = {}) { | ||||
|   let slackUrl = nconf.get('SLACK_URL'); | ||||
|  | ||||
|   if (!slackUrl) { | ||||
|     console.error('No slack post url specified. Your message was:'); | ||||
|     console.log(msg); | ||||
|     console.error('No slack post url specified. Your message was:'); // eslint-disable-line no-console | ||||
|     console.log(msg); // eslint-disable-line no-console | ||||
|  | ||||
|     return; | ||||
|   } | ||||
| @@ -89,15 +98,15 @@ export function postToSlack (msg, config = {}) { | ||||
|       channel: `#${config.channel || '#general'}`, | ||||
|       username: config.username || 'gulp task', | ||||
|       text: msg, | ||||
|       icon_emoji: `:${config.emoji || 'gulp'}:`, | ||||
|       icon_emoji: `:${config.emoji || 'gulp'}:`, // eslint-disable-line camelcase | ||||
|     }) | ||||
|     .end((err, res) => { | ||||
|       if (err) console.error('Unable to post to slack', err); | ||||
|     .end((err) => { | ||||
|       if (err) console.error('Unable to post to slack', err); // eslint-disable-line no-console | ||||
|     }); | ||||
| } | ||||
|  | ||||
| export function runMochaTests (files, server, cb) { | ||||
|   require('../test/helpers/globals.helper'); | ||||
|   require('../test/helpers/globals.helper'); // eslint-disable-line global-require | ||||
|  | ||||
|   let mocha = new Mocha({reporter: 'spec'}); | ||||
|   let tests = glob(files); | ||||
| @@ -108,7 +117,7 @@ export function runMochaTests (files, server, cb) { | ||||
|   }); | ||||
|  | ||||
|   mocha.run((numberOfFailures) => { | ||||
|     if (!process.env.RUN_INTEGRATION_TEST_FOREVER) { | ||||
|     if (!process.env.RUN_INTEGRATION_TEST_FOREVER) { // eslint-disable-line no-process-env | ||||
|       if (server) kill(server); | ||||
|       process.exit(numberOfFailures); | ||||
|     } | ||||
|   | ||||
							
								
								
									
										14
									
								
								gulpfile.js
									
									
									
									
									
								
							
							
						
						
									
										14
									
								
								gulpfile.js
									
									
									
									
									
								
							| @@ -8,13 +8,11 @@ | ||||
|  | ||||
| require('babel-register'); | ||||
|  | ||||
| if (process.env.NODE_ENV === 'production') { | ||||
|   require('./gulp/gulp-apidoc'); | ||||
|   require('./gulp/gulp-newstuff'); | ||||
|   require('./gulp/gulp-build'); | ||||
|   require('./gulp/gulp-babelify'); | ||||
|   require('./gulp/gulp-bootstrap'); | ||||
| if (process.env.NODE_ENV === 'production') { // eslint-disable-line no-process-env | ||||
|   require('./gulp/gulp-apidoc'); // eslint-disable-line global-require | ||||
|   require('./gulp/gulp-build'); // eslint-disable-line global-require | ||||
|   require('./gulp/gulp-bootstrap'); // eslint-disable-line global-require | ||||
| } else { | ||||
|   require('glob').sync('./gulp/gulp-*').forEach(require); | ||||
|   require('gulp').task('default', ['test']); | ||||
|   require('glob').sync('./gulp/gulp-*').forEach(require); // eslint-disable-line global-require | ||||
|   require('gulp').task('default', ['test']); // eslint-disable-line global-require | ||||
| } | ||||
|   | ||||
| @@ -1,4 +1,5 @@ | ||||
| var updateStore = require('../website/common/script/libs/updateStore'); | ||||
| import { selectGearToPin } from '../website/common/script/ops/pinnedGearUtils'; | ||||
|  | ||||
| var getItemInfo = require('../website/common/script/libs/getItemInfo'); | ||||
|  | ||||
| var migrationName = '20170928_redesign_launch.js'; | ||||
| @@ -69,7 +70,7 @@ function updateUser (user) { | ||||
|  | ||||
|   var set = {'migration': migrationName}; | ||||
|  | ||||
|   var oldRewardsList = updateStore(user); | ||||
|   var oldRewardsList = selectGearToPin(user); | ||||
|   var newPinnedItems = [ | ||||
|     { | ||||
|       type: 'armoire', | ||||
|   | ||||
							
								
								
									
										111
									
								
								migrations/20171030_jackolanterns.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										111
									
								
								migrations/20171030_jackolanterns.js
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,111 @@ | ||||
| var migrationName = '20171030_jackolanterns.js'; | ||||
| var authorName = 'Sabe'; // in case script author needs to know when their ... | ||||
| var authorUuid = '7f14ed62-5408-4e1b-be83-ada62d504931'; //... own data is done | ||||
|  | ||||
| /* | ||||
|  * Award the Jack-O'-Lantern ladder: | ||||
|  * Ghost Jack-O-Lantern Mount to owners of Ghost Jack-O-Lantern Pet | ||||
|  * Ghost Jack-O-Lantern Pet to owners of Jack-O-Lantern Mount | ||||
|  * Jack-O-Lantern Mount to owners of Jack-O-Lantern Pet | ||||
|  * Jack-O-Lantern Pet to everyone else | ||||
|  */ | ||||
|  | ||||
| var monk = require('monk'); | ||||
| var connectionString = 'mongodb://localhost:27017/habitrpg?auto_reconnect=true'; // FOR TEST DATABASE | ||||
| var dbUsers = monk(connectionString).get('users', { castIds: false }); | ||||
|  | ||||
| function processUsers(lastId) { | ||||
|   // specify a query to limit the affected users (empty for all users): | ||||
|   var query = { | ||||
|     'migration':{$ne:migrationName}, | ||||
|   }; | ||||
|  | ||||
|   if (lastId) { | ||||
|     query._id = { | ||||
|       $gt: lastId | ||||
|     } | ||||
|   } | ||||
|  | ||||
|   dbUsers.find(query, { | ||||
|     sort: {_id: 1}, | ||||
|     limit: 250, | ||||
|     fields: [ | ||||
|       'items.pets', | ||||
|       'items.mounts', | ||||
|     ] // specify fields we are interested in to limit retrieved data (empty if we're not reading data): | ||||
|   }) | ||||
|   .then(updateUsers) | ||||
|   .catch(function (err) { | ||||
|     console.log(err); | ||||
|     return exiting(1, 'ERROR! ' + err); | ||||
|   }); | ||||
| } | ||||
|  | ||||
| var progressCount = 1000; | ||||
| var count = 0; | ||||
|  | ||||
| function updateUsers (users) { | ||||
|   if (!users || users.length === 0) { | ||||
|     console.warn('All appropriate users found and modified.'); | ||||
|     displayData(); | ||||
|     return; | ||||
|   } | ||||
|  | ||||
|   var userPromises = users.map(updateUser); | ||||
|   var lastUser = users[users.length - 1]; | ||||
|  | ||||
|   return Promise.all(userPromises) | ||||
|   .then(function () { | ||||
|     processUsers(lastUser._id); | ||||
|   }); | ||||
| } | ||||
|  | ||||
| function updateUser (user) { | ||||
|   count++; | ||||
|  | ||||
|   var set = {}; | ||||
|   var inc = { | ||||
|     'items.food.Candy_Skeleton': 1, | ||||
|     'items.food.Candy_Base': 1, | ||||
|     'items.food.Candy_CottonCandyBlue': 1, | ||||
|     'items.food.Candy_CottonCandyPink': 1, | ||||
|     'items.food.Candy_Shade': 1, | ||||
|     'items.food.Candy_White': 1, | ||||
|     'items.food.Candy_Golden': 1, | ||||
|     'items.food.Candy_Zombie': 1, | ||||
|     'items.food.Candy_Desert': 1, | ||||
|     'items.food.Candy_Red': 1, | ||||
|   }; | ||||
|  | ||||
|   if (user && user.items && user.items.pets && user.items.pets['JackOLantern-Ghost']) { | ||||
|     set = {'migration':migrationName, 'items.mounts.JackOLantern-Ghost': true}; | ||||
|   } else if (user && user.items && user.items.mounts && user.items.mounts['JackOLantern-Base']) { | ||||
|     set = {'migration':migrationName, 'items.pets.JackOLantern-Ghost': 5}; | ||||
|   } else if (user && user.items && user.items.pets && user.items.pets['JackOLantern-Base']) { | ||||
|     set = {'migration':migrationName, 'items.mounts.JackOLantern-Base': true}; | ||||
|   } else { | ||||
|     set = {'migration':migrationName, 'items.pets.JackOLantern-Base': 5}; | ||||
|   } | ||||
|  | ||||
|   dbUsers.update({_id: user._id}, {$set:set, $inc:inc}); | ||||
|  | ||||
|   if (count % progressCount == 0) console.warn(count + ' ' + user._id); | ||||
|   if (user._id == authorUuid) console.warn(authorName + ' processed'); | ||||
| } | ||||
|  | ||||
| function displayData() { | ||||
|   console.warn('\n' + count + ' users processed\n'); | ||||
|   return exiting(0); | ||||
| } | ||||
|  | ||||
| function exiting(code, msg) { | ||||
|   code = code || 0; // 0 = success | ||||
|   if (code && !msg) { msg = 'ERROR!'; } | ||||
|   if (msg) { | ||||
|     if (code) { console.error(msg); } | ||||
|     else      { console.log(  msg); } | ||||
|   } | ||||
|   process.exit(code); | ||||
| } | ||||
|  | ||||
| module.exports = processUsers; | ||||
| @@ -2,7 +2,7 @@ var _id = ''; | ||||
| var update = { | ||||
|   $addToSet: { | ||||
|     'purchased.plan.mysteryItems':{ | ||||
|       $each:['shield_mystery_201709','back_mystery_201709'] | ||||
|       $each:['armor_mystery_201710','head_mystery_201710'] | ||||
|     } | ||||
|   } | ||||
| }; | ||||
|   | ||||
| @@ -65,19 +65,29 @@ function updateUser (user) { | ||||
|     set = {'migration':migrationName}; | ||||
|   } else if (typeof user.items.gear.owned.body_special_takeThis !== 'undefined') { | ||||
|     set = {'migration':migrationName, 'items.gear.owned.back_special_takeThis':false}; | ||||
|     var push = {pinnedItems: {type: 'marketGear', path: 'gear.flat.back_special_takeThis', '_id': monk.id()}}; | ||||
|   } else if (typeof user.items.gear.owned.head_special_takeThis !== 'undefined') { | ||||
|     set = {'migration':migrationName, 'items.gear.owned.body_special_takeThis':false}; | ||||
|     var push = {pinnedItems: {type: 'marketGear', path: 'gear.flat.body_special_takeThis', '_id': monk.id()}}; | ||||
|   } else if (typeof user.items.gear.owned.armor_special_takeThis !== 'undefined') { | ||||
|     set = {'migration':migrationName, 'items.gear.owned.head_special_takeThis':false}; | ||||
|     var push = {pinnedItems: {type: 'marketGear', path: 'gear.flat.head_special_takeThis', '_id': monk.id()}}; | ||||
|   } else if (typeof user.items.gear.owned.weapon_special_takeThis !== 'undefined') { | ||||
|     set = {'migration':migrationName, 'items.gear.owned.armor_special_takeThis':false}; | ||||
|     var push = {pinnedItems: {type: 'marketGear', path: 'gear.flat.armor_special_takeThis', '_id': monk.id()}}; | ||||
|   } else if (typeof user.items.gear.owned.shield_special_takeThis !== 'undefined') { | ||||
|     set = {'migration':migrationName, 'items.gear.owned.weapon_special_takeThis':false}; | ||||
|     var push = {pinnedItems: {type: 'marketGear', path: 'gear.flat.weapon_special_takeThis', '_id': monk.id()}}; | ||||
|   } else { | ||||
|     set = {'migration':migrationName, 'items.gear.owned.shield_special_takeThis':false}; | ||||
|     var push = {pinnedItems: {type: 'marketGear', path: 'gear.flat.shield_special_takeThis', '_id': monk.id()}}; | ||||
|   } | ||||
|  | ||||
|   dbUsers.update({_id: user._id}, {$set:set}); | ||||
|   if (push) { | ||||
|     dbUsers.update({_id: user._id}, {$set: set, $push: push}); | ||||
|   } else { | ||||
|     dbUsers.update({_id: user._id}, {$set: set}); | ||||
|   } | ||||
|  | ||||
|   if (count % progressCount == 0) console.warn(count + ' ' + user._id); | ||||
|   if (user._id == authorUuid) console.warn(authorName + ' processed'); | ||||
|   | ||||
							
								
								
									
										6523
									
								
								package-lock.json
									
									
									
										generated
									
									
									
								
							
							
						
						
									
										6523
									
								
								package-lock.json
									
									
									
										generated
									
									
									
								
							
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							
							
								
								
									
										34
									
								
								package.json
									
									
									
									
									
								
							
							
						
						
									
										34
									
								
								package.json
									
									
									
									
									
								
							| @@ -1,7 +1,7 @@ | ||||
| { | ||||
|   "name": "habitica", | ||||
|   "description": "A habit tracker app which treats your goals like a Role Playing Game.", | ||||
|   "version": "4.0.4", | ||||
|   "version": "4.9.0", | ||||
|   "main": "./website/server/index.js", | ||||
|   "dependencies": { | ||||
|     "@slack/client": "^3.8.1", | ||||
| @@ -14,6 +14,7 @@ | ||||
|     "autoprefixer": "^6.4.0", | ||||
|     "aws-sdk": "^2.0.25", | ||||
|     "axios": "^0.16.0", | ||||
|     "axios-progress-bar": "^0.1.7", | ||||
|     "babel-core": "^6.0.0", | ||||
|     "babel-eslint": "^7.2.3", | ||||
|     "babel-loader": "^6.0.0", | ||||
| @@ -31,8 +32,7 @@ | ||||
|     "bluebird": "^3.3.5", | ||||
|     "body-parser": "^1.15.0", | ||||
|     "bootstrap": "4.0.0-alpha.6", | ||||
|     "bootstrap-vue": "^1.0.0-beta.6", | ||||
|     "bower": "~1.3.12", | ||||
|     "bootstrap-vue": "1.0.0-beta.7", | ||||
|     "browserify": "~12.0.1", | ||||
|     "compression": "^1.6.1", | ||||
|     "connect-ratelimit": "0.0.7", | ||||
| @@ -52,18 +52,8 @@ | ||||
|     "file-loader": "^0.10.0", | ||||
|     "glob": "^4.3.5", | ||||
|     "got": "^6.1.1", | ||||
|     "grunt": "~0.4.1", | ||||
|     "grunt-cli": "~0.1.9", | ||||
|     "grunt-contrib-clean": "~0.6.0", | ||||
|     "grunt-contrib-copy": "~0.6.0", | ||||
|     "grunt-contrib-cssmin": "~0.10.0", | ||||
|     "grunt-contrib-stylus": "~0.20.0", | ||||
|     "grunt-contrib-uglify": "~0.6.0", | ||||
|     "grunt-contrib-watch": "~0.6.1", | ||||
|     "grunt-hashres": "git://github.com/habitrpg/grunt-hashres#dc85db6d3002e29e1b7c5ee186b80d708d2f0e0b", | ||||
|     "gulp": "^3.9.0", | ||||
|     "gulp-babel": "^6.1.2", | ||||
|     "gulp-grunt": "^0.5.2", | ||||
|     "gulp-imagemin": "^2.4.0", | ||||
|     "gulp-nodemon": "^2.0.4", | ||||
|     "gulp-sourcemaps": "^1.6.0", | ||||
| @@ -126,12 +116,12 @@ | ||||
|     "validator": "^4.9.0", | ||||
|     "vinyl-buffer": "^1.0.0", | ||||
|     "vinyl-source-stream": "^1.1.0", | ||||
|     "vue": "^2.1.0", | ||||
|     "vue-loader": "^11.0.0", | ||||
|     "vue": "git://github.com/habitrpg/vue#e8f45fcfc98ed1859669f3a6a8b23ae28bf4d46f", | ||||
|     "vue-loader": "^13.3.0", | ||||
|     "vue-mugen-scroll": "^0.2.1", | ||||
|     "vue-router": "^2.0.0-rc.5", | ||||
|     "vue-router": "^3.0.0", | ||||
|     "vue-style-loader": "^3.0.0", | ||||
|     "vue-template-compiler": "^2.1.10", | ||||
|     "vue-template-compiler": "^2.5.2", | ||||
|     "vuejs-datepicker": "git://github.com/habitrpg/vuejs-datepicker#45e607a7bccf4e3e089761b3b7b33e3f2c5dc21f", | ||||
|     "webpack": "^2.2.1", | ||||
|     "webpack-merge": "^4.0.0", | ||||
| @@ -146,7 +136,7 @@ | ||||
|   }, | ||||
|   "scripts": { | ||||
|     "lint": "eslint --ext .js,.vue .", | ||||
|     "test": "npm run lint && gulp test && npm run client:unit && gulp apidoc", | ||||
|     "test": "npm run lint && gulp test && gulp apidoc", | ||||
|     "test:build": "gulp test:prepare:build", | ||||
|     "test:api-v3": "gulp test:api-v3", | ||||
|     "test:api-v3:unit": "gulp test:api-v3:unit", | ||||
| @@ -155,11 +145,6 @@ | ||||
|     "test:sanity": "istanbul cover --dir coverage/sanity --report lcovonly node_modules/mocha/bin/_mocha -- test/sanity --recursive", | ||||
|     "test:common": "istanbul cover --dir coverage/common --report lcovonly node_modules/mocha/bin/_mocha -- test/common --recursive", | ||||
|     "test:content": "istanbul cover --dir coverage/content --report lcovonly node_modules/mocha/bin/_mocha -- test/content --recursive", | ||||
|     "test:karma": "karma start test/client-old/spec/karma.conf.js --single-run", | ||||
|     "test:karma:watch": "karma start test/client-old/spec/karma.conf.js", | ||||
|     "test:prepare:webdriver": "webdriver-manager update", | ||||
|     "test:e2e:webdriver": "webdriver-manager start", | ||||
|     "test:e2e": "protractor test/client-old/e2e/protractor.conf.js", | ||||
|     "test:nodemon": "gulp test:nodemon", | ||||
|     "coverage": "COVERAGE=true mocha --require register-handlers.js --reporter html-cov > coverage.html; open coverage.html", | ||||
|     "sprites": "gulp sprites:compile", | ||||
| @@ -169,7 +154,7 @@ | ||||
|     "client:unit:watch": "cross-env NODE_ENV=test karma start test/client/unit/karma.conf.js", | ||||
|     "client:e2e": "node test/client/e2e/runner.js", | ||||
|     "client:test": "npm run client:unit && npm run client:e2e", | ||||
|     "start": "gulp run:dev", | ||||
|     "start": "gulp nodemon", | ||||
|     "postinstall": "gulp build", | ||||
|     "apidoc": "gulp apidoc" | ||||
|   }, | ||||
| @@ -193,7 +178,6 @@ | ||||
|     "event-stream": "^3.2.2", | ||||
|     "eventsource-polyfill": "^0.9.6", | ||||
|     "expect.js": "~0.2.0", | ||||
|     "grunt-karma": "~0.12.1", | ||||
|     "http-proxy-middleware": "^0.17.0", | ||||
|     "inject-loader": "^3.0.0-beta4", | ||||
|     "istanbul": "^1.1.0-alpha.1", | ||||
|   | ||||
| @@ -142,4 +142,22 @@ describe('GET /challenges/:challengeId/members', () => { | ||||
|     let resIds = res.concat(res2).map(member => member._id); | ||||
|     expect(resIds).to.eql(expectedIds.sort()); | ||||
|   }); | ||||
|  | ||||
|   it('supports using req.query.search to get search members', async () => { | ||||
|     let group = await generateGroup(user, {type: 'party', name: generateUUID()}); | ||||
|     let challenge = await generateChallenge(user, group); | ||||
|  | ||||
|     let usersToGenerate = []; | ||||
|     for (let i = 0; i < 3; i++) { | ||||
|       usersToGenerate.push(generateUser({challenges: [challenge._id]})); | ||||
|     } | ||||
|     let generatedUsers = await Promise.all(usersToGenerate); | ||||
|     let profileNames = generatedUsers.map(generatedUser => generatedUser.profile.name); | ||||
|  | ||||
|     let firstProfileName = profileNames[0]; | ||||
|     let nameToSearch = firstProfileName.substring(0, 4); | ||||
|  | ||||
|     let response = await user.get(`/challenges/${challenge._id}/members?search=${nameToSearch}`); | ||||
|     expect(response[0].profile.name).to.eql(firstProfileName); | ||||
|   }); | ||||
| }); | ||||
|   | ||||
| @@ -4,6 +4,7 @@ import { | ||||
|   createAndPopulateGroup, | ||||
|   translate as t, | ||||
| } from '../../../../helpers/api-v3-integration.helper'; | ||||
| import { TAVERN_ID } from '../../../../../website/common/script/constants'; | ||||
|  | ||||
| describe('GET challenges/groups/:groupId', () => { | ||||
|   context('Public Guild', () => { | ||||
| @@ -181,4 +182,123 @@ describe('GET challenges/groups/:groupId', () => { | ||||
|       expect(foundChallengeIndex).to.eql(1); | ||||
|     }); | ||||
|   }); | ||||
|  | ||||
|   context('Party', () => { | ||||
|     let party, user, nonMember, challenge, challenge2; | ||||
|  | ||||
|     before(async () => { | ||||
|       let { group, groupLeader } = await createAndPopulateGroup({ | ||||
|         groupDetails: { | ||||
|           name: 'TestParty', | ||||
|           type: 'party', | ||||
|         }, | ||||
|       }); | ||||
|  | ||||
|       party = group; | ||||
|       user = groupLeader; | ||||
|  | ||||
|       nonMember = await generateUser(); | ||||
|  | ||||
|       challenge = await generateChallenge(user, group); | ||||
|       challenge2 = await generateChallenge(user, group); | ||||
|     }); | ||||
|  | ||||
|     it('should prevent non-member from seeing challenges', async () => { | ||||
|       await expect(nonMember.get(`/challenges/groups/${party._id}`)) | ||||
|         .to.eventually.be.rejected.and.eql({ | ||||
|           code: 404, | ||||
|           error: 'NotFound', | ||||
|           message: t('groupNotFound'), | ||||
|         }); | ||||
|     }); | ||||
|  | ||||
|     it('should return group challenges for member with populated leader', async () => { | ||||
|       let challenges = await user.get(`/challenges/groups/${party._id}`); | ||||
|  | ||||
|       let foundChallenge1 = _.find(challenges, { _id: challenge._id }); | ||||
|       expect(foundChallenge1).to.exist; | ||||
|       expect(foundChallenge1.leader).to.eql({ | ||||
|         _id: party.leader._id, | ||||
|         id: party.leader._id, | ||||
|         profile: {name: user.profile.name}, | ||||
|       }); | ||||
|       let foundChallenge2 = _.find(challenges, { _id: challenge2._id }); | ||||
|       expect(foundChallenge2).to.exist; | ||||
|       expect(foundChallenge2.leader).to.eql({ | ||||
|         _id: party.leader._id, | ||||
|         id: party.leader._id, | ||||
|         profile: {name: user.profile.name}, | ||||
|       }); | ||||
|     }); | ||||
|  | ||||
|     it('should return group challenges for member using ID "party"', async () => { | ||||
|       let challenges = await user.get('/challenges/groups/party'); | ||||
|  | ||||
|       let foundChallenge1 = _.find(challenges, { _id: challenge._id }); | ||||
|       expect(foundChallenge1).to.exist; | ||||
|       expect(foundChallenge1.leader).to.eql({ | ||||
|         _id: party.leader._id, | ||||
|         id: party.leader._id, | ||||
|         profile: {name: user.profile.name}, | ||||
|       }); | ||||
|       let foundChallenge2 = _.find(challenges, { _id: challenge2._id }); | ||||
|       expect(foundChallenge2).to.exist; | ||||
|       expect(foundChallenge2.leader).to.eql({ | ||||
|         _id: party.leader._id, | ||||
|         id: party.leader._id, | ||||
|         profile: {name: user.profile.name}, | ||||
|       }); | ||||
|     }); | ||||
|   }); | ||||
|  | ||||
|   context('Tavern', () => { | ||||
|     let tavern, user, challenge, challenge2; | ||||
|  | ||||
|     before(async () => { | ||||
|       user = await generateUser(); | ||||
|       await user.update({balance: 0.5}); | ||||
|       tavern = await user.get(`/groups/${TAVERN_ID}`); | ||||
|  | ||||
|       challenge = await generateChallenge(user, tavern, {prize: 1}); | ||||
|       challenge2 = await generateChallenge(user, tavern, {prize: 1}); | ||||
|     }); | ||||
|  | ||||
|     it('should return tavern challenges with populated leader', async () => { | ||||
|       let challenges = await user.get(`/challenges/groups/${TAVERN_ID}`); | ||||
|  | ||||
|       let foundChallenge1 = _.find(challenges, { _id: challenge._id }); | ||||
|       expect(foundChallenge1).to.exist; | ||||
|       expect(foundChallenge1.leader).to.eql({ | ||||
|         _id: user._id, | ||||
|         id: user._id, | ||||
|         profile: {name: user.profile.name}, | ||||
|       }); | ||||
|       let foundChallenge2 = _.find(challenges, { _id: challenge2._id }); | ||||
|       expect(foundChallenge2).to.exist; | ||||
|       expect(foundChallenge2.leader).to.eql({ | ||||
|         _id: user._id, | ||||
|         id: user._id, | ||||
|         profile: {name: user.profile.name}, | ||||
|       }); | ||||
|     }); | ||||
|  | ||||
|     it('should return tavern challenges using ID "habitrpg', async () => { | ||||
|       let challenges = await user.get('/challenges/groups/habitrpg'); | ||||
|  | ||||
|       let foundChallenge1 = _.find(challenges, { _id: challenge._id }); | ||||
|       expect(foundChallenge1).to.exist; | ||||
|       expect(foundChallenge1.leader).to.eql({ | ||||
|         _id: user._id, | ||||
|         id: user._id, | ||||
|         profile: {name: user.profile.name}, | ||||
|       }); | ||||
|       let foundChallenge2 = _.find(challenges, { _id: challenge2._id }); | ||||
|       expect(foundChallenge2).to.exist; | ||||
|       expect(foundChallenge2.leader).to.eql({ | ||||
|         _id: user._id, | ||||
|         id: user._id, | ||||
|         profile: {name: user.profile.name}, | ||||
|       }); | ||||
|     }); | ||||
|   }); | ||||
| }); | ||||
|   | ||||
| @@ -4,7 +4,7 @@ import { | ||||
| } from '../../../../helpers/api-v3-integration.helper'; | ||||
| import { v4 as generateUUID } from 'uuid'; | ||||
|  | ||||
| describe('GET /export/avatar-:memberId.html', () => { | ||||
| xdescribe('GET /export/avatar-:memberId.html', () => { | ||||
|   let user; | ||||
|  | ||||
|   before(async () => { | ||||
|   | ||||
| @@ -98,4 +98,24 @@ describe('POST /user/purchase/:type/:key', () => { | ||||
|     await members[0].sync(); | ||||
|     expect(members[0].balance).to.equal(oldBalance); | ||||
|   }); | ||||
|  | ||||
|   describe('bulk purchasing', () => { | ||||
|     it('purchases a gem item', async () => { | ||||
|       await user.post(`/user/purchase/${type}/${key}`, {quantity: 2}); | ||||
|       await user.sync(); | ||||
|  | ||||
|       expect(user.items[type][key]).to.equal(2); | ||||
|     }); | ||||
|  | ||||
|     it('can convert gold to gems if subscribed', async () => { | ||||
|       let oldBalance = user.balance; | ||||
|       await user.update({ | ||||
|         'purchased.plan.customerId': 'group-plan', | ||||
|         'stats.gp': 1000, | ||||
|       }); | ||||
|       await user.post('/user/purchase/gems/gem', {quantity: 2}); | ||||
|       await user.sync(); | ||||
|       expect(user.balance).to.equal(oldBalance + 0.50); | ||||
|     }); | ||||
|   }); | ||||
| }); | ||||
|   | ||||
| @@ -3,8 +3,8 @@ | ||||
| import { | ||||
|   generateUser, | ||||
|   translate as t, | ||||
| } from '../../../../helpers/api-integration/v3'; | ||||
| import shared from '../../../../../website/common/script'; | ||||
| } from '../../../../../helpers/api-integration/v3'; | ||||
| import shared from '../../../../../../website/common/script'; | ||||
| 
 | ||||
| let content = shared.content; | ||||
| 
 | ||||
| @@ -56,6 +56,7 @@ describe('POST /user/buy/:key', () => { | ||||
|         message: t('messageHealthAlreadyMax'), | ||||
|       }); | ||||
|   }); | ||||
| 
 | ||||
|   it('buys a piece of gear', async () => { | ||||
|     let key = 'armor_warrior_1'; | ||||
| 
 | ||||
| @@ -64,4 +65,36 @@ describe('POST /user/buy/:key', () => { | ||||
| 
 | ||||
|     expect(user.items.gear.owned.armor_warrior_1).to.eql(true); | ||||
|   }); | ||||
| 
 | ||||
|   it('buys a special spell', async () => { | ||||
|     let key = 'spookySparkles'; | ||||
|     let item = content.special[key]; | ||||
| 
 | ||||
|     await user.update({'stats.gp': 250}); | ||||
|     let res = await user.post(`/user/buy/${key}`); | ||||
|     await user.sync(); | ||||
| 
 | ||||
|     expect(res.data).to.eql({ | ||||
|       items: JSON.parse(JSON.stringify(user.items)), // otherwise dates can't be compared
 | ||||
|       stats: user.stats, | ||||
|     }); | ||||
|     expect(res.message).to.equal(t('messageBought', { | ||||
|       itemText: item.text(), | ||||
|     })); | ||||
|   }); | ||||
| 
 | ||||
|   it('allows for bulk purchases', async () => { | ||||
|     await user.update({ | ||||
|       'stats.gp': 400, | ||||
|       'stats.hp': 20, | ||||
|     }); | ||||
| 
 | ||||
|     let potion = content.potion; | ||||
|     let res = await user.post('/user/buy/potion', {quantity: 2}); | ||||
|     await user.sync(); | ||||
| 
 | ||||
|     expect(user.stats.hp).to.equal(50); | ||||
|     expect(res.data).to.eql(user.stats); | ||||
|     expect(res.message).to.equal(t('messageBought', {itemText: potion.text()})); | ||||
|   }); | ||||
| }); | ||||
| @@ -1,7 +1,7 @@ | ||||
| import { | ||||
|   generateUser, | ||||
|   translate as t, | ||||
| } from '../../../../helpers/api-integration/v3'; | ||||
| } from '../../../../../helpers/api-integration/v3'; | ||||
| 
 | ||||
| describe('POST /user/buy-armoire', () => { | ||||
|   let user; | ||||
| @@ -3,7 +3,7 @@ | ||||
| import { | ||||
|   generateUser, | ||||
|   translate as t, | ||||
| } from '../../../../helpers/api-integration/v3'; | ||||
| } from '../../../../../helpers/api-integration/v3'; | ||||
| 
 | ||||
| describe('POST /user/buy-gear/:key', () => { | ||||
|   let user; | ||||
| @@ -1,8 +1,8 @@ | ||||
| import { | ||||
|   generateUser, | ||||
|   translate as t, | ||||
| } from '../../../../helpers/api-integration/v3'; | ||||
| import shared from '../../../../../website/common/script'; | ||||
| } from '../../../../../helpers/api-integration/v3'; | ||||
| import shared from '../../../../../../website/common/script'; | ||||
| 
 | ||||
| let content = shared.content; | ||||
| 
 | ||||
| @@ -1,7 +1,7 @@ | ||||
| import { | ||||
|   generateUser, | ||||
|   translate as t, | ||||
| } from '../../../../helpers/api-integration/v3'; | ||||
| } from '../../../../../helpers/api-integration/v3'; | ||||
| 
 | ||||
| describe('POST /user/buy-mystery-set/:key', () => { | ||||
|   let user; | ||||
| @@ -1,8 +1,8 @@ | ||||
| import { | ||||
|   generateUser, | ||||
|   translate as t, | ||||
| } from '../../../../helpers/api-integration/v3'; | ||||
| import shared from '../../../../../website/common/script'; | ||||
| } from '../../../../../helpers/api-integration/v3'; | ||||
| import shared from '../../../../../../website/common/script'; | ||||
| 
 | ||||
| let content = shared.content; | ||||
| 
 | ||||
| @@ -1,8 +1,8 @@ | ||||
| import { | ||||
|   generateUser, | ||||
|   translate as t, | ||||
| } from '../../../../helpers/api-integration/v3'; | ||||
| import shared from '../../../../../website/common/script'; | ||||
| } from '../../../../../helpers/api-integration/v3'; | ||||
| import shared from '../../../../../../website/common/script'; | ||||
| 
 | ||||
| let content = shared.content; | ||||
| 
 | ||||
| @@ -1,7 +1,7 @@ | ||||
| import { | ||||
|   generateUser, | ||||
|   translate as t, | ||||
| } from '../../../../helpers/api-integration/v3'; | ||||
| } from '../../../../../helpers/api-integration/v3'; | ||||
| 
 | ||||
| describe('POST /user/allocate', () => { | ||||
|   let user; | ||||
| @@ -0,0 +1,40 @@ | ||||
| import { | ||||
|   generateUser, | ||||
|   translate as t, | ||||
| } from '../../../../../helpers/api-integration/v3'; | ||||
|  | ||||
| describe('POST /user/allocate-bulk', () => { | ||||
|   let user; | ||||
|   const statsUpdate = { | ||||
|     stats: { | ||||
|       con: 1, | ||||
|       str: 2, | ||||
|     }, | ||||
|   }; | ||||
|  | ||||
|   beforeEach(async () => { | ||||
|     user = await generateUser(); | ||||
|   }); | ||||
|  | ||||
|   // More tests in common code unit tests | ||||
|  | ||||
|   it('returns an error if user does not have enough points', async () => { | ||||
|     await expect(user.post('/user/allocate-bulk', statsUpdate)) | ||||
|       .to.eventually.be.rejected.and.eql({ | ||||
|         code: 401, | ||||
|         error: 'NotAuthorized', | ||||
|         message: t('notEnoughAttrPoints'), | ||||
|       }); | ||||
|   }); | ||||
|  | ||||
|   it('allocates attribute points', async () => { | ||||
|     await user.update({'stats.points': 3}); | ||||
|  | ||||
|     await user.post('/user/allocate-bulk', statsUpdate); | ||||
|     await user.sync(); | ||||
|  | ||||
|     expect(user.stats.con).to.equal(1); | ||||
|     expect(user.stats.str).to.equal(2); | ||||
|     expect(user.stats.points).to.equal(0); | ||||
|   }); | ||||
| }); | ||||
| @@ -1,6 +1,6 @@ | ||||
| import { | ||||
|   generateUser, | ||||
| } from '../../../../helpers/api-integration/v3'; | ||||
| } from '../../../../../helpers/api-integration/v3'; | ||||
| 
 | ||||
| describe('POST /user/allocate-now', () => { | ||||
|   // More tests in common code unit tests
 | ||||
| @@ -1,31 +0,0 @@ | ||||
| import { | ||||
|   getManifestFiles, | ||||
| } from '../../../../../website/server/libs/buildManifest'; | ||||
|  | ||||
| describe('Build Manifest', () => { | ||||
|   describe('getManifestFiles', () => { | ||||
|     it('returns an html string', () => { | ||||
|       let htmlCode = getManifestFiles('app'); | ||||
|  | ||||
|       expect(htmlCode.startsWith('<script') || htmlCode.startsWith('<link')).to.be.true; | ||||
|     }); | ||||
|  | ||||
|     it('can return only js files', () => { | ||||
|       let htmlCode = getManifestFiles('app', 'js'); | ||||
|  | ||||
|       expect(htmlCode.indexOf('<link') === -1).to.be.true; | ||||
|     }); | ||||
|  | ||||
|     it('can return only css files', () => { | ||||
|       let htmlCode = getManifestFiles('app', 'css'); | ||||
|  | ||||
|       expect(htmlCode.indexOf('<script') === -1).to.be.true; | ||||
|     }); | ||||
|  | ||||
|     it('throws an error in case the page does not exist', () => { | ||||
|       expect(() => { | ||||
|         getManifestFiles('strange name here'); | ||||
|       }).to.throw(Error); | ||||
|     }); | ||||
|   }); | ||||
| }); | ||||
| @@ -365,6 +365,72 @@ describe('cron', () => { | ||||
|  | ||||
|       expect(user.history.todos).to.be.lengthOf(1); | ||||
|     }); | ||||
|  | ||||
|     it('should remove completed todos from users taskOrder list', () => { | ||||
|       tasksByType.todos = []; | ||||
|       user.tasksOrder.todos = []; | ||||
|       let todo = { | ||||
|         text: 'test todo', | ||||
|         type: 'todo', | ||||
|         value: 0, | ||||
|       }; | ||||
|  | ||||
|       let task = new Tasks.todo(Tasks.Task.sanitize(todo)); // eslint-disable-line new-cap | ||||
|       tasksByType.todos.push(task); | ||||
|       task = new Tasks.todo(Tasks.Task.sanitize(todo)); // eslint-disable-line new-cap | ||||
|       tasksByType.todos.push(task); | ||||
|       tasksByType.todos[0].completed = true; | ||||
|  | ||||
|       user.tasksOrder.todos = tasksByType.todos.map(taskTodo => { | ||||
|         return taskTodo._id; | ||||
|       }); | ||||
|       // Since ideally tasksByType should not contain completed todos, fake ids should be filtered too | ||||
|       user.tasksOrder.todos.push('00000000-0000-0000-0000-000000000000'); | ||||
|  | ||||
|       expect(tasksByType.todos).to.be.lengthOf(2); | ||||
|       expect(user.tasksOrder.todos).to.be.lengthOf(3); | ||||
|  | ||||
|       cron({user, tasksByType, daysMissed, analytics}); | ||||
|  | ||||
|       // user.tasksOrder.todos should be filtered while tasks by type remains unchanged | ||||
|       expect(tasksByType.todos).to.be.lengthOf(2); | ||||
|       expect(user.tasksOrder.todos).to.be.lengthOf(1); | ||||
|     }); | ||||
|  | ||||
|     it('should preserve todos order in task list', () => { | ||||
|       tasksByType.todos = []; | ||||
|       user.tasksOrder.todos = []; | ||||
|       let todo = { | ||||
|         text: 'test todo', | ||||
|         type: 'todo', | ||||
|         value: 0, | ||||
|       }; | ||||
|  | ||||
|       let task = new Tasks.todo(Tasks.Task.sanitize(todo)); // eslint-disable-line new-cap | ||||
|       tasksByType.todos.push(task); | ||||
|       task = new Tasks.todo(Tasks.Task.sanitize(todo)); // eslint-disable-line new-cap | ||||
|       tasksByType.todos.push(task); | ||||
|       task = new Tasks.todo(Tasks.Task.sanitize(todo)); // eslint-disable-line new-cap | ||||
|       tasksByType.todos.push(task); | ||||
|  | ||||
|       // Set up user.tasksOrder list in a specific order | ||||
|       user.tasksOrder.todos = tasksByType.todos.map(todoTask => { | ||||
|         return todoTask._id; | ||||
|       }).reverse(); | ||||
|       let original = user.tasksOrder.todos; // Preserve the original order | ||||
|  | ||||
|       cron({user, tasksByType, daysMissed, analytics}); | ||||
|  | ||||
|       let listsAreEqual = true; | ||||
|       user.tasksOrder.todos.forEach((taskId, index) => { | ||||
|         if (original[index]._id !== taskId) { | ||||
|           listsAreEqual = false; | ||||
|         } | ||||
|       }); | ||||
|  | ||||
|       expect(listsAreEqual); | ||||
|       expect(user.tasksOrder.todos).to.be.lengthOf(original.length); | ||||
|     }); | ||||
|   }); | ||||
|  | ||||
|   describe('dailys', () => { | ||||
|   | ||||
| @@ -475,7 +475,7 @@ describe('Purchasing a group plan for group', () => { | ||||
|  | ||||
|     let updatedUser = await User.findById(recipient._id).exec(); | ||||
|  | ||||
|     expect(updatedUser.purchased.plan.extraMonths).to.within(3, 4); | ||||
|     expect(updatedUser.purchased.plan.extraMonths).to.within(3, 5); | ||||
|   }); | ||||
|  | ||||
|   it('adds months to members with existing recurring subscription (Paypal)', async () => { | ||||
|   | ||||
| @@ -13,6 +13,9 @@ import analyticsService from '../../../../../website/server/libs/analyticsServic | ||||
| import * as cronLib from '../../../../../website/server/libs/cron'; | ||||
| import { v4 as generateUUID } from 'uuid'; | ||||
|  | ||||
| const CRON_TIMEOUT_WAIT = new Date(60 * 60 * 1000).getTime(); | ||||
| const CRON_TIMEOUT_UNIT = new Date(60 * 1000).getTime(); | ||||
|  | ||||
| describe('cron middleware', () => { | ||||
|   let res, req; | ||||
|   let user; | ||||
| @@ -235,7 +238,13 @@ describe('cron middleware', () => { | ||||
|     sandbox.spy(cronLib, 'recoverCron'); | ||||
|  | ||||
|     sandbox.stub(User, 'update') | ||||
|       .withArgs({ _id: user._id, _cronSignature: 'NOT_RUNNING' }) | ||||
|       .withArgs({ | ||||
|         _id: user._id, | ||||
|         $or: [ | ||||
|           {_cronSignature: 'NOT_RUNNING'}, | ||||
|           {_cronSignature: {$lt: sinon.match.number}}, | ||||
|         ], | ||||
|       }) | ||||
|       .returns({ | ||||
|         exec () { | ||||
|           return Promise.resolve(updatedUser); | ||||
| @@ -251,4 +260,48 @@ describe('cron middleware', () => { | ||||
|       }); | ||||
|     }); | ||||
|   }); | ||||
|  | ||||
|   it('cronSignature less than an hour ago should error', async () => { | ||||
|     user.lastCron = moment(new Date()).subtract({days: 2}); | ||||
|     let now = new Date(); | ||||
|     await User.update({ | ||||
|       _id: user._id, | ||||
|     }, { | ||||
|       $set: { | ||||
|         _cronSignature: now.getTime() - CRON_TIMEOUT_WAIT + CRON_TIMEOUT_UNIT, | ||||
|       }, | ||||
|     }).exec(); | ||||
|     await user.save(); | ||||
|     let expectedErrMessage = `Impossible to recover from cron for user ${user._id}.`; | ||||
|  | ||||
|     await new Promise((resolve, reject) => { | ||||
|       cronMiddleware(req, res, (err) => { | ||||
|         if (!err) return reject(new Error('Cron should have failed.')); | ||||
|         expect(err.message).to.be.equal(expectedErrMessage); | ||||
|         resolve(); | ||||
|       }); | ||||
|     }); | ||||
|   }); | ||||
|  | ||||
|   it('cronSignature longer than an hour ago should allow cron', async () => { | ||||
|     user.lastCron = moment(new Date()).subtract({days: 2}); | ||||
|     let now = new Date(); | ||||
|     await User.update({ | ||||
|       _id: user._id, | ||||
|     }, { | ||||
|       $set: { | ||||
|         _cronSignature: now.getTime() - CRON_TIMEOUT_WAIT - CRON_TIMEOUT_UNIT, | ||||
|       }, | ||||
|     }).exec(); | ||||
|     await user.save(); | ||||
|  | ||||
|     await new Promise((resolve, reject) => { | ||||
|       cronMiddleware(req, res, (err) => { | ||||
|         if (err) return reject(err); | ||||
|         expect(moment(now).isSame(user.auth.timestamps.loggedin, 'day')); | ||||
|         expect(user._cronSignature).to.be.equal('NOT_RUNNING'); | ||||
|         resolve(); | ||||
|       }); | ||||
|     }); | ||||
|   }); | ||||
| }); | ||||
|   | ||||
| @@ -4,6 +4,7 @@ import { | ||||
|   generateNext, | ||||
| } from '../../../../helpers/api-unit.helper'; | ||||
| import responseMiddleware from '../../../../../website/server/middlewares/response'; | ||||
| import packageInfo from '../../../../../package.json'; | ||||
|  | ||||
| describe('response middleware', () => { | ||||
|   let res, req, next; | ||||
| @@ -34,6 +35,7 @@ describe('response middleware', () => { | ||||
|       data: {field: 1}, | ||||
|       notifications: [], | ||||
|       userV: res.locals.user._v, | ||||
|       appVersion: packageInfo.version, | ||||
|     }); | ||||
|   }); | ||||
|  | ||||
| @@ -51,6 +53,7 @@ describe('response middleware', () => { | ||||
|       message: 'hello', | ||||
|       notifications: [], | ||||
|       userV: res.locals.user._v, | ||||
|       appVersion: packageInfo.version, | ||||
|     }); | ||||
|   }); | ||||
|  | ||||
| @@ -67,6 +70,7 @@ describe('response middleware', () => { | ||||
|       data: {field: 1}, | ||||
|       notifications: [], | ||||
|       userV: res.locals.user._v, | ||||
|       appVersion: packageInfo.version, | ||||
|     }); | ||||
|   }); | ||||
|  | ||||
| @@ -81,6 +85,7 @@ describe('response middleware', () => { | ||||
|       data: {field: 1}, | ||||
|       notifications: [], | ||||
|       userV: 0, | ||||
|       appVersion: packageInfo.version, | ||||
|     }); | ||||
|   }); | ||||
|  | ||||
| @@ -104,6 +109,7 @@ describe('response middleware', () => { | ||||
|         }, | ||||
|       ], | ||||
|       userV: res.locals.user._v, | ||||
|       appVersion: packageInfo.version, | ||||
|     }); | ||||
|   }); | ||||
| }); | ||||
|   | ||||
| @@ -282,7 +282,7 @@ describe('Group Model', () => { | ||||
|           expect(finishQuest).to.be.calledWith(quest); | ||||
|         }); | ||||
|  | ||||
|         context('with Rage', () => { | ||||
|         context('with healing Rage', () => { | ||||
|           beforeEach(async () => { | ||||
|             party.quest.active = false; | ||||
|             party.quest.key = 'trex_undead'; | ||||
| @@ -327,6 +327,46 @@ describe('Group Model', () => { | ||||
|             expect(party.quest.progress.hp).to.eql(500); | ||||
|           }); | ||||
|         }); | ||||
|  | ||||
|         context('with Mana drain Rage', () => { | ||||
|           beforeEach(async () => { | ||||
|             party.quest.active = false; | ||||
|             party.quest.key = 'lostMasterclasser4'; | ||||
|  | ||||
|             await party.startQuest(questLeader); | ||||
|             await party.save(); | ||||
|           }); | ||||
|  | ||||
|           it('applies down progress to boss rage', async () => { | ||||
|             progress.down = -2; | ||||
|  | ||||
|             await Group.processQuestProgress(participatingMember, progress); | ||||
|  | ||||
|             party = await Group.findOne({_id: party._id}); | ||||
|  | ||||
|             expect(party.quest.progress.rage).to.eql(8); | ||||
|  | ||||
|             let drainedUser = await User.findById(participatingMember._id); | ||||
|             expect(drainedUser.stats.mp).to.eql(10); | ||||
|           }); | ||||
|  | ||||
|           it('activates rage when progress.down triggers rage bar', async () => { | ||||
|             let quest = questScrolls[party.quest.key]; | ||||
|  | ||||
|             progress.down = -999; | ||||
|  | ||||
|             await party.save(); | ||||
|             await Group.processQuestProgress(participatingMember, progress); | ||||
|  | ||||
|             party = await Group.findOne({_id: party._id}); | ||||
|  | ||||
|             expect(Group.prototype.sendChat).to.be.calledWith(quest.boss.rage.effect('en')); | ||||
|             expect(party.quest.progress.rage).to.eql(0); | ||||
|  | ||||
|             let drainedUser = await User.findById(participatingMember._id); | ||||
|             expect(drainedUser.stats.mp).to.eql(0); | ||||
|           }); | ||||
|         }); | ||||
|       }); | ||||
|  | ||||
|       context('Collection Quests', () => { | ||||
| @@ -1342,6 +1382,42 @@ describe('Group Model', () => { | ||||
|         expect(updatedParticipatingMember.achievements.quests[quest.key]).to.eql(1); | ||||
|       }); | ||||
|  | ||||
|       it('gives out super awesome Masterclasser achievement to the deserving', async () => { | ||||
|         quest = questScrolls.lostMasterclasser4; | ||||
|         party.quest.key = quest.key; | ||||
|  | ||||
|         questLeader.achievements.quests = { | ||||
|           mayhemMistiflying1: 1, | ||||
|           mayhemMistiflying2: 1, | ||||
|           mayhemMistiflying3: 1, | ||||
|           stoikalmCalamity1: 1, | ||||
|           stoikalmCalamity2: 1, | ||||
|           stoikalmCalamity3: 1, | ||||
|           taskwoodsTerror1: 1, | ||||
|           taskwoodsTerror2: 1, | ||||
|           taskwoodsTerror3: 1, | ||||
|           dilatoryDistress1: 1, | ||||
|           dilatoryDistress2: 1, | ||||
|           dilatoryDistress3: 1, | ||||
|           lostMasterclasser1: 1, | ||||
|           lostMasterclasser2: 1, | ||||
|           lostMasterclasser3: 1, | ||||
|         }; | ||||
|         await questLeader.save(); | ||||
|         await party.finishQuest(quest); | ||||
|  | ||||
|         let [ | ||||
|           updatedLeader, | ||||
|           updatedParticipatingMember, | ||||
|         ] = await Promise.all([ | ||||
|           User.findById(questLeader._id), | ||||
|           User.findById(participatingMember._id), | ||||
|         ]); | ||||
|  | ||||
|         expect(updatedLeader.achievements.lostMasterclasser).to.eql(true); | ||||
|         expect(updatedParticipatingMember.achievements.lostMasterclasser).to.not.eql(true); | ||||
|       }); | ||||
|  | ||||
|       it('gives xp and gold', async () => { | ||||
|         await party.finishQuest(quest); | ||||
|  | ||||
|   | ||||
| @@ -1,7 +0,0 @@ | ||||
| { | ||||
|     "globals": { | ||||
|       "browser": true, | ||||
|       "by": true, | ||||
|       "element": true | ||||
|     } | ||||
| } | ||||
| @@ -1,22 +0,0 @@ | ||||
| import fs from 'fs'; | ||||
| import { resetHabiticaDB } from '../../helpers/mongo'; | ||||
|  | ||||
| before(async () => { | ||||
|   await resetHabiticaDB(); | ||||
| }); | ||||
|  | ||||
| // based on https://github.com/angular/protractor/issues/114#issuecomment-29046939 | ||||
| afterEach(async function () { | ||||
|   let lastTest = this.currentTest; | ||||
|  | ||||
|   if (lastTest.state === 'failed') { | ||||
|     let filename = `exception_${lastTest.title}.png`; | ||||
|     let png = await browser.takeScreenshot(); | ||||
|     let buffer = new Buffer(png, 'base64'); | ||||
|     let stream = fs.createWriteStream(filename); | ||||
|  | ||||
|     stream.write(buffer); | ||||
|     stream.end(); | ||||
|   } | ||||
| }); | ||||
|  | ||||
| @@ -1,30 +0,0 @@ | ||||
| 'use strict'; | ||||
|  | ||||
| let chai = require('chai'); | ||||
| let chaiAsPromised = require('chai-as-promised'); | ||||
|  | ||||
| require('babel-register'); | ||||
| require('babel-polyfill'); | ||||
|  | ||||
| exports.config = { | ||||
|   specs: ['./helper.js', './**/*.test.js'], | ||||
|   baseUrl: 'http://localhost:3003/', | ||||
|   capabilities: { | ||||
|     browserName: 'firefox', | ||||
|   }, | ||||
|   directConnect: true, | ||||
|   seleniumAddress: 'http://localhost:4444/wd/hub', | ||||
|   framework: 'mocha', | ||||
|   mochaOpts: { | ||||
|     reporter: 'spec', | ||||
|     slow: 6000, | ||||
|     timeout: 10000, | ||||
|     compilers: 'js:babel-register', | ||||
|   }, | ||||
|   onPrepare: () => { | ||||
|     browser.ignoreSynchronization = true; | ||||
|  | ||||
|     chai.use(chaiAsPromised); | ||||
|     global.expect = chai.expect; | ||||
|   }, | ||||
| }; | ||||
| @@ -1,64 +0,0 @@ | ||||
| import { v4 as generateUniqueId } from 'uuid'; | ||||
|  | ||||
| describe('Static Front Page', () => { | ||||
|   beforeEach(() => { | ||||
|     browser.get('/'); | ||||
|     browser.sleep(1000); | ||||
|   }); | ||||
|  | ||||
|   it('shows the front page', async () => { | ||||
|     let button = element(by.id('play-btn')); | ||||
|  | ||||
|     await expect(button.getText()).to.eventually.eql('Join for free'); | ||||
|   }); | ||||
|  | ||||
|   it('does not login when using wrong credentials', async () => { | ||||
|     let button = element(by.id('play-btn')); | ||||
|     let randomName = generateUniqueId(); | ||||
|  | ||||
|     button.click(); | ||||
|     browser.sleep(1000); | ||||
|  | ||||
|     element(by.model('loginUsername')).sendKeys(randomName); | ||||
|     element(by.model('loginPassword')).sendKeys('pass'); | ||||
|  | ||||
|     let login = element(by.css('#loginForm input[value="Login"]')); | ||||
|  | ||||
|     login.click(); | ||||
|     browser.sleep(1000); | ||||
|  | ||||
|     let alertDialog = browser.switchTo().alert(); | ||||
|  | ||||
|     await expect(alertDialog.getText()).to.eventually.match(/username or password is incorrect./); | ||||
|  | ||||
|     alertDialog.accept(); | ||||
|   }); | ||||
|  | ||||
|   it('registers a new user', async function () { | ||||
|     this.timeout(30000); // TODO: Speed up registration action. Takes way too long and times out unless you extend the timeout | ||||
|  | ||||
|     let button = element(by.id('play-btn')); | ||||
|     let randomName = generateUniqueId(); | ||||
|  | ||||
|     button.click(); | ||||
|     browser.sleep(1000); | ||||
|  | ||||
|     let registerTab = element(by.linkText('Register')); | ||||
|     registerTab.click(); | ||||
|     element(by.model('registerVals.username')).sendKeys(randomName); | ||||
|     element(by.model('registerVals.email')).sendKeys(`${randomName}@example.com`); | ||||
|     element(by.model('registerVals.password')).sendKeys('pass'); | ||||
|     element(by.model('registerVals.confirmPassword')).sendKeys('pass'); | ||||
|  | ||||
|     let register = element(by.css('#registrationForm input[type="submit"]')); | ||||
|  | ||||
|     register.click(); | ||||
|     browser.sleep(3000); | ||||
|  | ||||
|     let url = await browser.getCurrentUrl(); | ||||
|  | ||||
|     expect(url).to.not.match(/static\/front/); | ||||
|   }); | ||||
|  | ||||
|   it('logs in an existing user'); | ||||
| }); | ||||
| @@ -1,31 +0,0 @@ | ||||
| 'use strict'; | ||||
|  | ||||
| describe('AppJS', function() { | ||||
|   describe('Automatic page refresh', function(){ | ||||
|     var clock; | ||||
|     beforeEach(function () { | ||||
|       clock = sandbox.useFakeTimers(); | ||||
|       sandbox.stub(window, "refresher", function(){return true}); | ||||
|     }); | ||||
|  | ||||
|     it('should not call refresher if idle time is less than 6 hours', function() { | ||||
|       window.awaitIdle(); | ||||
|       clock.tick(21599999); | ||||
|       expect(window.refresher).to.not.be.called; | ||||
|     }); | ||||
|  | ||||
|     it('should not call refresher if awaitIdle is called within 6 hours', function() { | ||||
|       window.awaitIdle(); | ||||
|       clock.tick(21500000); | ||||
|       window.awaitIdle(); | ||||
|       clock.tick(21500000); | ||||
|       expect(window.refresher).to.not.be.called; | ||||
|     }); | ||||
|  | ||||
|     it('should call refresher if idle time is 6 hours or greater', function() { | ||||
|       window.awaitIdle(); | ||||
|       clock.tick(21900000); | ||||
|       expect(window.refresher).to.be.called; | ||||
|     }); | ||||
|   }); | ||||
| }); | ||||
| @@ -1,125 +0,0 @@ | ||||
| 'use strict'; | ||||
|  | ||||
| describe('Auth Controller', function() { | ||||
|   var scope, ctrl, user, $httpBackend, $window, $modal, alert, Auth; | ||||
|  | ||||
|   beforeEach(function(){ | ||||
|     module(function($provide) { | ||||
|       Auth = { | ||||
|         runAuth: sandbox.spy(), | ||||
|       }; | ||||
|       $provide.value('Analytics', analyticsMock); | ||||
|       $provide.value('Chat', { seenMessage: function() {} }); | ||||
|       $provide.value('Auth', Auth); | ||||
|     }); | ||||
|  | ||||
|     inject(function(_$httpBackend_, $rootScope, $controller, _$modal_) { | ||||
|       $httpBackend = _$httpBackend_; | ||||
|       scope = $rootScope.$new(); | ||||
|       scope.loginUsername = 'user'; | ||||
|       scope.loginPassword = 'pass'; | ||||
|       $window = { location: { href: ""}, alert: sandbox.spy() }; | ||||
|       $modal = _$modal_; | ||||
|       user = { user: {}, authenticate: sandbox.spy() }; | ||||
|       alert = { authErrorAlert: sandbox.spy() }; | ||||
|  | ||||
|       ctrl = $controller('AuthCtrl', {$scope: scope, $window: $window, User: user, Alert: alert}); | ||||
|     }) | ||||
|   }); | ||||
|  | ||||
|   describe('logging in', function() { | ||||
|     it('should log in users with correct uname / pass', function() { | ||||
|       $httpBackend.expectPOST('/api/v3/user/auth/local/login').respond({data: {id: 'abc', apiToken: 'abc'}}); | ||||
|       scope.auth(); | ||||
|       $httpBackend.flush(); | ||||
|       expect(Auth.runAuth).to.be.calledOnce; | ||||
|       expect(alert.authErrorAlert).to.not.be.called; | ||||
|     }); | ||||
|  | ||||
|     it('should not log in users with incorrect uname / pass', function() { | ||||
|       $httpBackend.expectPOST('/api/v3/user/auth/local/login').respond(404, ''); | ||||
|       scope.auth(); | ||||
|       $httpBackend.flush(); | ||||
|       expect(Auth.runAuth).to.not.be.called; | ||||
|       expect(alert.authErrorAlert).to.be.calledOnce; | ||||
|     }); | ||||
|   }); | ||||
|  | ||||
|   describe('#clearLocalStorage', function () { | ||||
|     var timer; | ||||
|  | ||||
|     beforeEach(function () { | ||||
|       timer = sandbox.useFakeTimers(); | ||||
|       sandbox.stub($modal, 'open'); | ||||
|     }); | ||||
|  | ||||
|     it('opens modal with message about clearing local storage and logging out', function () { | ||||
|       scope.clearLocalStorage(); | ||||
|  | ||||
|       expect($modal.open).to.be.calledOnce; | ||||
|       expect($modal.open).to.be.calledWith({ | ||||
|         templateUrl: 'modals/message-modal.html', | ||||
|         scope: scope | ||||
|       }); | ||||
|  | ||||
|       expect(scope.messageModal.title).to.eql(window.env.t('localStorageClearing')); | ||||
|       expect(scope.messageModal.body).to.eql(window.env.t('localStorageClearingExplanation')); | ||||
|     }); | ||||
|  | ||||
|     it('does not call $scope.logout before 3 seconds', function () { | ||||
|       sandbox.stub(scope, 'logout'); | ||||
|  | ||||
|       scope.clearLocalStorage(); | ||||
|  | ||||
|       timer.tick(2999); | ||||
|  | ||||
|       expect(scope.logout).to.not.be.called; | ||||
|     }); | ||||
|  | ||||
|     it('calls $scope.logout after 3 seconds', function () { | ||||
|       sandbox.stub(scope, 'logout'); | ||||
|  | ||||
|       scope.clearLocalStorage(); | ||||
|  | ||||
|       timer.tick(3000); | ||||
|  | ||||
|       expect(scope.logout).to.be.calledOnce; | ||||
|     }); | ||||
|  | ||||
|     it('does not clear local storage before 3 seconds', function () { | ||||
|       sandbox.stub(localStorage, 'clear'); | ||||
|  | ||||
|       scope.clearLocalStorage(); | ||||
|  | ||||
|       timer.tick(2999); | ||||
|  | ||||
|       expect(localStorage.clear).to.not.be.called; | ||||
|     }); | ||||
|  | ||||
|     it('clears local storage after 3 seconds', function () { | ||||
|       sandbox.stub(localStorage, 'clear'); | ||||
|  | ||||
|       scope.clearLocalStorage(); | ||||
|  | ||||
|       timer.tick(3000); | ||||
|  | ||||
|       expect(localStorage.clear).to.be.calledOnce; | ||||
|     }); | ||||
|  | ||||
|     it('does not redirect to /logout route before 3 seconds', function () { | ||||
|       scope.clearLocalStorage(); | ||||
|  | ||||
|       timer.tick(2999); | ||||
|  | ||||
|       expect($window.location.href).to.eql(''); | ||||
|     }); | ||||
|  | ||||
|     it('redirects to /logout after 3 seconds', function () { | ||||
|       scope.clearLocalStorage(); | ||||
|  | ||||
|       timer.tick(3000); | ||||
|  | ||||
|       expect($window.location.href).to.eql('/logout'); | ||||
|     }); | ||||
|   }); | ||||
| }); | ||||
| @@ -1,120 +0,0 @@ | ||||
| 'use strict'; | ||||
|  | ||||
| describe("Autocomplete controller", function() { | ||||
|   var scope, ctrl, user, $rootScope, $controller; | ||||
|  | ||||
|   beforeEach(function() { | ||||
|     module(function($provide) { | ||||
|       $provide.value('User', {}); | ||||
|     }); | ||||
|  | ||||
|     inject(function($rootScope, _$controller_){ | ||||
|       user = specHelper.newUser(); | ||||
|       user._id = "unique-user-id"; | ||||
|  | ||||
|       scope = $rootScope.$new(); | ||||
|       scope.group = {} | ||||
|       scope.group.chat = []; | ||||
|  | ||||
|       $controller = _$controller_; | ||||
|  | ||||
|       // Load RootCtrl to ensure shared behaviors are loaded | ||||
|       $controller('RootCtrl',  {$scope: scope, User: {user: user}}); | ||||
|  | ||||
|       ctrl = $controller('AutocompleteCtrl', {$scope: scope}); | ||||
|     }); | ||||
|   }); | ||||
|  | ||||
|   describe("clearUserList", function() { | ||||
|     it('calling the function clears the list of usernames and responses', function() { | ||||
|       scope.response.push("blah"); | ||||
|       scope.usernames.push("blub"); | ||||
|  | ||||
|       scope.clearUserlist(); | ||||
|       expect(scope.response).to.be.empty; | ||||
|       expect(scope.usernames).to.be.empty; | ||||
|     }); | ||||
|  | ||||
|     it('the function is called upon initialization of the controller', function() { | ||||
|       scope.response.push("blah"); | ||||
|       scope.response.push("blub"); | ||||
|       ctrl = $controller('AutocompleteCtrl', {$scope: scope}); | ||||
|  | ||||
|       expect(scope.response).to.be.empty; | ||||
|       expect(scope.usernames).to.be.empty; | ||||
|     }); | ||||
|   }) | ||||
|  | ||||
|   describe("filterUser", function() { | ||||
|     it('filters with undefined query (not loaded yet) and returns false (so it will not be rendered)', function() { | ||||
|         expect(scope.filterUser({user: "boo"})).to.be.eq(false); | ||||
|     }); | ||||
|  | ||||
|     it('filters with null query (no typing yet) and returns false (so it will not be rendered)', function() { | ||||
|       scope.query = null | ||||
|       expect(scope.filterUser({user: "boo"})).to.be.eq(false); | ||||
|     }); | ||||
|  | ||||
|     it('filters with empty prefix and returns true', function() { | ||||
|       scope.query = {text: ""}; | ||||
|       expect(scope.filterUser({user: "prefix"})).to.be.eq(true); | ||||
|     }); | ||||
|  | ||||
|     it('filters with prefix element and returns true', function() { | ||||
|       scope.query = {text: "pre"} | ||||
|       expect(scope.filterUser({user: "prefix"})).to.be.eq(true); | ||||
|     }); | ||||
|  | ||||
|     it('filters with prefix element of a different case and returns true', function() { | ||||
|       scope.query = {text: "pre"} | ||||
|       expect(scope.filterUser({user: "Prefix"})).to.be.eq(true); | ||||
|     }); | ||||
|  | ||||
|     it('filters with nonprefix element and returns false', function() { | ||||
|       scope.query = {text: "noprefix"} | ||||
|       expect(scope.filterUser({user: "prefix"})).to.be.eq(false); | ||||
|     }); | ||||
|  | ||||
|     it('filters out system messages (messages without username)', function() { | ||||
|       scope.query = {text: "myquery"} | ||||
|       expect(scope.filterUser({uuid: "system"})).to.be.eq(false); | ||||
|     }); | ||||
|   }); | ||||
|  | ||||
|   describe("performCompletion", function() { | ||||
|     it('triggers autoComplete', function() { | ||||
|       scope.autoComplete = sandbox.spy(); | ||||
|  | ||||
|       var msg = {user: "boo"}; // scope.autoComplete only cares about user | ||||
|       scope.query = {text: "b"}; | ||||
|       scope.performCompletion(msg); | ||||
|  | ||||
|       expect(scope.query).to.be.eq(null); | ||||
|       expect(scope.autoComplete.callCount).to.be.eq(1); | ||||
|       expect(scope.autoComplete).to.have.been.calledWith(msg); | ||||
|     }); | ||||
|   }); | ||||
|  | ||||
|   describe("addNewUser", function() { | ||||
|     it('a new message from a new user will modify the usernames', function() { | ||||
|       expect(scope.response).to.be.empty; | ||||
|       expect(scope.usernames).to.be.empty; | ||||
|  | ||||
|       var msg = {user: "boo"}; | ||||
|       scope.addNewUser(msg); | ||||
|       expect(scope.response[0]).to.be.eq(msg); | ||||
|       expect(scope.usernames[0]).to.be.eq("boo"); | ||||
|     }); | ||||
|   }); | ||||
|  | ||||
|   describe("chatChanged", function() { | ||||
|     it('if a new chat arrives, the new user name is extracted', function() { | ||||
|       var chatChanged = sandbox.spy(scope, 'chatChanged'); | ||||
|       scope.$watch('group.chat',scope.chatChanged); // reinstantiate watch so spy works | ||||
|  | ||||
|       scope.$digest(); // trigger watch | ||||
|       scope.group.chat.push({msg: "new chat", user: "boo"}); | ||||
|       expect(chatChanged.callCount).to.be.eq(1); | ||||
|     }); | ||||
|   }); | ||||
| }); | ||||
| @@ -1,774 +0,0 @@ | ||||
| 'use strict'; | ||||
|  | ||||
| describe('Challenges Controller', function() { | ||||
|   var rootScope, scope, user, User, ctrl, groups, members, notification, state, challenges, tasks, tavernId; | ||||
|  | ||||
|   beforeEach(function() { | ||||
|     module(function($provide) { | ||||
|       user = specHelper.newUser(); | ||||
|       User = { | ||||
|         getBalanceInGems: sandbox.stub(), | ||||
|         sync: sandbox.stub(), | ||||
|         user: user | ||||
|       } | ||||
|       $provide.value('User', User); | ||||
|     }); | ||||
|  | ||||
|     inject(function($rootScope, $controller, _$state_, _Groups_, _Members_, _Notification_, _Challenges_, _Tasks_, _TAVERN_ID_){ | ||||
|       scope = $rootScope.$new(); | ||||
|       rootScope = $rootScope; | ||||
|  | ||||
|       // Load RootCtrl to ensure shared behaviors are loaded | ||||
|       $controller('RootCtrl',  {$scope: scope, User: User}); | ||||
|  | ||||
|       ctrl = $controller('ChallengesCtrl', {$scope: scope, User: User}); | ||||
|  | ||||
|       challenges = _Challenges_; | ||||
|       tasks = _Tasks_; | ||||
|       groups = _Groups_; | ||||
|       members = _Members_; | ||||
|       notification = _Notification_; | ||||
|       state = _$state_; | ||||
|       tavernId = _TAVERN_ID_; | ||||
|     }); | ||||
|   }); | ||||
|  | ||||
|   context('filtering', function() { | ||||
|     describe('filterChallenges', function() { | ||||
|       var ownMem, ownNotMem, notOwnMem, notOwnNotMem; | ||||
|  | ||||
|       beforeEach(function() { | ||||
|         ownMem = specHelper.newChallenge({ | ||||
|           description: 'You are the owner and member', | ||||
|           leader: user._id, | ||||
|           members: [user], | ||||
|           _isMember: true, | ||||
|           _id: 'ownMem-id', | ||||
|         }); | ||||
|  | ||||
|         ownNotMem = specHelper.newChallenge({ | ||||
|           description: 'You are the owner, but not a member', | ||||
|           leader: user._id, | ||||
|           members: [], | ||||
|           _isMember: false, | ||||
|           _id: 'ownNotMem-id', | ||||
|         }); | ||||
|  | ||||
|         notOwnMem = specHelper.newChallenge({ | ||||
|           description: 'Not owner but a member', | ||||
|           leader: {_id:"test"}, | ||||
|           members: [user], | ||||
|           _isMember: true, | ||||
|           _id: 'notOwnMem-id', | ||||
|         }); | ||||
|  | ||||
|         notOwnNotMem = specHelper.newChallenge({ | ||||
|           description: 'Not owner or member', | ||||
|           leader: {_id:"test"}, | ||||
|           members: [], | ||||
|           _isMember: false, | ||||
|           _id: 'notOwnNotMem-id', | ||||
|         }); | ||||
|  | ||||
|         user.challenges = [ownMem._id, notOwnMem._id]; | ||||
|  | ||||
|         scope.search = { | ||||
|           group: _.transform(groups, function(m,g){m[g._id]=true;}) | ||||
|         }; | ||||
|       }); | ||||
|  | ||||
|       it('displays challenges that match membership: either and owner: either', function() { | ||||
|         scope.search._isMember = 'either'; | ||||
|         scope.search._isOwner = 'either'; | ||||
|         expect(scope.filterChallenges(ownMem)).to.eql(true); | ||||
|         expect(scope.filterChallenges(ownNotMem)).to.eql(true); | ||||
|         expect(scope.filterChallenges(notOwnMem)).to.eql(true); | ||||
|         expect(scope.filterChallenges(notOwnNotMem)).to.eql(true); | ||||
|       }); | ||||
|  | ||||
|       it('displays challenges that match membership: either and owner: true', function() { | ||||
|         scope.search._isMember = 'either'; | ||||
|         scope.search._isOwner = true; | ||||
|         expect(scope.filterChallenges(ownMem)).to.eql(true); | ||||
|         expect(scope.filterChallenges(ownNotMem)).to.eql(true); | ||||
|         expect(scope.filterChallenges(notOwnMem)).to.eql(false); | ||||
|         expect(scope.filterChallenges(notOwnNotMem)).to.eql(false); | ||||
|       }); | ||||
|  | ||||
|       it('displays challenges that match membership: either and owner: false', function() { | ||||
|         scope.search._isMember = 'either'; | ||||
|         scope.search._isOwner = false; | ||||
|         expect(scope.filterChallenges(ownMem)).to.eql(false); | ||||
|         expect(scope.filterChallenges(ownNotMem)).to.eql(false); | ||||
|         expect(scope.filterChallenges(notOwnMem)).to.eql(true); | ||||
|         expect(scope.filterChallenges(notOwnNotMem)).to.eql(true); | ||||
|       }); | ||||
|  | ||||
|       it('displays challenges that match membership: true and owner: either', function() { | ||||
|         scope.search._isMember = true; | ||||
|         scope.search._isOwner = 'either'; | ||||
|         expect(scope.filterChallenges(ownMem)).to.eql(true); | ||||
|         expect(scope.filterChallenges(ownNotMem)).to.eql(false); | ||||
|         expect(scope.filterChallenges(notOwnMem)).to.eql(true); | ||||
|         expect(scope.filterChallenges(notOwnNotMem)).to.eql(false); | ||||
|       }); | ||||
|  | ||||
|       it('displays challenges that match membership: true and owner: true', function() { | ||||
|         scope.search._isMember = true; | ||||
|         scope.search._isOwner = true; | ||||
|         expect(scope.filterChallenges(ownMem)).to.eql(true); | ||||
|         expect(scope.filterChallenges(ownNotMem)).to.eql(false); | ||||
|         expect(scope.filterChallenges(notOwnMem)).to.eql(false); | ||||
|         expect(scope.filterChallenges(notOwnNotMem)).to.eql(false); | ||||
|       }); | ||||
|  | ||||
|       it('displays challenges that match membership: true and owner: false', function() { | ||||
|         scope.search._isMember = true; | ||||
|         scope.search._isOwner = false; | ||||
|         expect(scope.filterChallenges(ownMem)).to.eql(false); | ||||
|         expect(scope.filterChallenges(ownNotMem)).to.eql(false); | ||||
|         expect(scope.filterChallenges(notOwnMem)).to.eql(true); | ||||
|         expect(scope.filterChallenges(notOwnNotMem)).to.eql(false); | ||||
|       }); | ||||
|  | ||||
|       it('displays challenges that match membership: false and owner: either', function() { | ||||
|         scope.search._isMember = false; | ||||
|         scope.search._isOwner = 'either'; | ||||
|         expect(scope.filterChallenges(ownMem)).to.eql(false); | ||||
|         expect(scope.filterChallenges(ownNotMem)).to.eql(true); | ||||
|         expect(scope.filterChallenges(notOwnMem)).to.eql(false); | ||||
|         expect(scope.filterChallenges(notOwnNotMem)).to.eql(true); | ||||
|       }); | ||||
|  | ||||
|       it('displays challenges that match membership: false and owner: true', function() { | ||||
|         scope.search._isMember = false; | ||||
|         scope.search._isOwner = true; | ||||
|         expect(scope.filterChallenges(ownMem)).to.eql(false); | ||||
|         expect(scope.filterChallenges(ownNotMem)).to.eql(true); | ||||
|         expect(scope.filterChallenges(notOwnMem)).to.eql(false); | ||||
|         expect(scope.filterChallenges(notOwnNotMem)).to.eql(false); | ||||
|       }); | ||||
|  | ||||
|       it('displays challenges that match membership: false and owner: false', function() { | ||||
|         scope.search._isMember = false; | ||||
|         scope.search._isOwner = false; | ||||
|         expect(scope.filterChallenges(ownMem)).to.eql(false); | ||||
|         expect(scope.filterChallenges(ownNotMem)).to.eql(false); | ||||
|         expect(scope.filterChallenges(notOwnMem)).to.eql(false); | ||||
|         expect(scope.filterChallenges(notOwnNotMem)).to.eql(true); | ||||
|       }); | ||||
|  | ||||
|       it('filters challenges to a single group when group id filter is set', inject(function($controller) { | ||||
|         scope.search = { }; | ||||
|         scope.groupsFilter = { | ||||
|           0: specHelper.newGroup({_id: 'group-one'}), | ||||
|           1: specHelper.newGroup({_id: 'group-two'}), | ||||
|           2: specHelper.newGroup({_id: 'group-three'}) | ||||
|         }; | ||||
|  | ||||
|         scope.groupIdFilter = 'group-one'; | ||||
|         scope.filterInitialChallenges(); | ||||
|         expect(scope.search.group).to.eql({'group-one': true}); | ||||
|       })); | ||||
|     }); | ||||
|  | ||||
|     describe('selectAll', function() { | ||||
|       it('sets all groups in seach.group to true', function() { | ||||
|         scope.search = { }; | ||||
|         scope.groupsFilter = { | ||||
|           0: specHelper.newGroup({_id: 'group-one'}), | ||||
|           1: specHelper.newGroup({_id: 'group-two'}), | ||||
|           2: specHelper.newGroup({_id: 'group-three'}) | ||||
|         }; | ||||
|         scope.selectAll(); | ||||
|  | ||||
|         expect(scope.search.group).to.eql({ | ||||
|           'group-one': true, | ||||
|           'group-two': true, | ||||
|           'group-three': true | ||||
|         }); | ||||
|       }); | ||||
|     }); | ||||
|  | ||||
|     describe('selectNone', function() { | ||||
|       it('sets all groups in seach.group to false', function() { | ||||
|         scope.search = { }; | ||||
|         scope.groupsFilter = { | ||||
|           0: specHelper.newGroup({_id: 'group-one'}), | ||||
|           1: specHelper.newGroup({_id: 'group-two'}), | ||||
|           2: specHelper.newGroup({_id: 'group-three'}) | ||||
|         }; | ||||
|         scope.selectNone(); | ||||
|  | ||||
|         expect(scope.search.group).to.eql({ | ||||
|           'group-one': false, | ||||
|           'group-two': false, | ||||
|           'group-three': false | ||||
|         }); | ||||
|       }); | ||||
|     }); | ||||
|   }); | ||||
|  | ||||
|   context('task manipulation', function() { | ||||
|  | ||||
|     describe('shouldShow', function() { | ||||
|       it('overrides task controller function by always returning true', function() { | ||||
|         expect(scope.shouldShow()).to.eq(true); | ||||
|       }); | ||||
|     }); | ||||
|  | ||||
|     describe('addTask', function() { | ||||
|       var challenge; | ||||
|  | ||||
|       beforeEach(function () { | ||||
|         challenge = specHelper.newChallenge({ | ||||
|           description: 'You are the owner and member', | ||||
|           leader: user._id, | ||||
|           members: [user], | ||||
|           _isMember: true | ||||
|         }); | ||||
|       }); | ||||
|  | ||||
|       it('adds default task to array', function() { | ||||
|         var taskArray = []; | ||||
|         var listDef = { | ||||
|           newTask: 'new todo text', | ||||
|           type: 'todo' | ||||
|         } | ||||
|  | ||||
|         scope.addTask(listDef, challenge); | ||||
|  | ||||
|         expect(challenge['todos'].length).to.eql(1); | ||||
|         expect(challenge['todos'][0].text).to.eql('new todo text'); | ||||
|         expect(challenge['todos'][0].type).to.eql('todo'); | ||||
|       }); | ||||
|  | ||||
|       it('adds the task to the front of the array', function() { | ||||
|         var previousTask = specHelper.newTodo({ text: 'previous task' }); | ||||
|         var taskArray = []; | ||||
|         challenge['todos'] = [previousTask]; | ||||
|         var listDef = { | ||||
|           newTask: 'new todo', | ||||
|           type: 'todo' | ||||
|         } | ||||
|  | ||||
|         scope.addTask(listDef, challenge); | ||||
|  | ||||
|         expect(challenge['todos'].length).to.eql(2); | ||||
|         expect(challenge['todos'][0].text).to.eql('new todo'); | ||||
|         expect(challenge['todos'][1].text).to.eql('previous task'); | ||||
|       }); | ||||
|  | ||||
|       it('removes text from new task input box', function() { | ||||
|         var taskArray = []; | ||||
|         var listDef = { | ||||
|           newTask: 'new todo text', | ||||
|           type: 'todo' | ||||
|         } | ||||
|  | ||||
|         scope.addTask(listDef, challenge); | ||||
|  | ||||
|         expect(listDef.newTask).to.not.exist; | ||||
|       }); | ||||
|     }); | ||||
|  | ||||
|     describe('editTask', function() { | ||||
|       it('is Tasks.editTask', function() { | ||||
|         inject(function(Tasks) { | ||||
|           // @TODO: Currently we override the task function in the challenge ctrl, but we should abstract it again | ||||
|           // expect(scope.editTask).to.eql(Tasks.editTask); | ||||
|         }); | ||||
|       }); | ||||
|     }); | ||||
|  | ||||
|     describe('removeTask', function() { | ||||
|       var task, challenge; | ||||
|  | ||||
|       beforeEach(function() { | ||||
|         sandbox.stub(window, 'confirm'); | ||||
|         task = specHelper.newTodo(); | ||||
|         challenge = specHelper.newChallenge({ | ||||
|           description: 'You are the owner and member', | ||||
|           leader: user._id, | ||||
|           members: [user], | ||||
|           _isMember: true | ||||
|         }); | ||||
|         challenge['todos'] = [task]; | ||||
|       }); | ||||
|  | ||||
|       it('asks user to confirm deletion', function() { | ||||
|         scope.removeTask(task, challenge); | ||||
|         expect(window.confirm).to.be.calledOnce; | ||||
|       }); | ||||
|  | ||||
|       it('does not remove task from list if not confirmed', function() { | ||||
|         window.confirm.returns(false); | ||||
|         scope.removeTask(task, challenge); | ||||
|  | ||||
|         expect(challenge['todos']).to.include(task); | ||||
|       }); | ||||
|  | ||||
|       it('removes task from list', function() { | ||||
|         window.confirm.returns(true); | ||||
|         scope.removeTask(task, challenge); | ||||
|  | ||||
|         expect(challenge['todos']).to.not.include(task); | ||||
|       }); | ||||
|     }); | ||||
|  | ||||
|     describe('saveTask', function() { | ||||
|       it('sets task._editing to false', function() { | ||||
|         var task = specHelper.newTask({ _editing: true }); | ||||
|  | ||||
|         scope.saveTask(task); | ||||
|  | ||||
|         expect(task._editing).to.be.eql(false); | ||||
|       }); | ||||
|     }); | ||||
|   }); | ||||
|  | ||||
|   context('challenge owner interactions', function() { | ||||
|     describe("save challenge", function() { | ||||
|       var alert, createChallengeSpy, challengeResponse, taskChallengeCreateSpy; | ||||
|  | ||||
|       beforeEach(function(){ | ||||
|         alert = sandbox.stub(window, "alert"); | ||||
|         createChallengeSpy = sinon.stub(challenges, 'createChallenge'); | ||||
|         challengeResponse = {data: {data: {_id: 'new-challenge'}}}; | ||||
|         createChallengeSpy.returns(Promise.resolve(challengeResponse)); | ||||
|  | ||||
|         taskChallengeCreateSpy = sinon.stub(tasks, 'createChallengeTasks'); | ||||
|         var taskResponse = {data: {data: []}}; | ||||
|         taskChallengeCreateSpy.returns(Promise.resolve(taskResponse)); | ||||
|       }); | ||||
|  | ||||
|       it("opens an alert box if challenge.group is not specified", function() { | ||||
|         var challenge = specHelper.newChallenge({ | ||||
|           name: 'Challenge without a group', | ||||
|           shortName: 'chal without group', | ||||
|           group: null | ||||
|         }); | ||||
|  | ||||
|         scope.save(challenge); | ||||
|  | ||||
|         expect(alert).to.be.calledOnce; | ||||
|         expect(alert).to.be.calledWith(window.env.t('selectGroup')); | ||||
|       }); | ||||
|  | ||||
|       it("opens an alert box if isNew and user does not have enough gems", function() { | ||||
|         var challenge = specHelper.newChallenge({ | ||||
|           name: 'Challenge without enough gems', | ||||
|           shortName: 'chal without gem', | ||||
|           prize: 5 | ||||
|         }); | ||||
|  | ||||
|         scope.maxPrize = 4; | ||||
|         scope.save(challenge); | ||||
|  | ||||
|         expect(alert).to.be.calledOnce; | ||||
|         expect(alert).to.be.calledWith(window.env.t('challengeNotEnoughGems')); | ||||
|       }); | ||||
|  | ||||
|       it("saves the challenge if user does not have enough gems, but the challenge is not new", function() { | ||||
|         var updateChallengeSpy = sinon.spy(challenges, 'updateChallenge'); | ||||
|  | ||||
|         var challenge = specHelper.newChallenge({ | ||||
|           _id: 'challenge-has-id-so-its-not-new', | ||||
|           name: 'Challenge without enough gems', | ||||
|           shortName: 'chal without gem', | ||||
|           prize: 5, | ||||
|         }); | ||||
|  | ||||
|         scope.maxPrize = 0; | ||||
|         scope.save(challenge); | ||||
|  | ||||
|         expect(updateChallengeSpy).to.be.calledOnce; | ||||
|         expect(alert).to.not.be.called; | ||||
|       }); | ||||
|  | ||||
|       it("saves the challenge if user has enough gems and challenge is new", function() { | ||||
|         var challenge = specHelper.newChallenge({ | ||||
|           name: 'Challenge without enough gems', | ||||
|           shortName: 'chal without gem', | ||||
|           prize: 5, | ||||
|         }); | ||||
|  | ||||
|         scope.maxPrize = 5; | ||||
|         scope.save(challenge); | ||||
|  | ||||
|         expect(createChallengeSpy).to.be.calledOnce; | ||||
|         expect(alert).to.not.be.called; | ||||
|       }); | ||||
|  | ||||
|       it('saves challenge and then proceeds to detail page', function(done) { | ||||
|         sandbox.stub(state, 'transitionTo'); | ||||
|  | ||||
|         var challenge = specHelper.newChallenge({ | ||||
|           name: 'Challenge', | ||||
|           shortName: 'chal', | ||||
|         }); | ||||
|  | ||||
|         setTimeout(function() { | ||||
|           expect(createChallengeSpy).to.be.calledOnce; | ||||
|           expect(state.transitionTo).to.be.calledWith( | ||||
|             'options.social.challenges.detail', | ||||
|             { cid: 'new-challenge' }, | ||||
|             { | ||||
|               reload: true, inherit: false, notify: true | ||||
|             } | ||||
|           ); | ||||
|           done(); | ||||
|         }, 1000); | ||||
|  | ||||
|         scope.save(challenge); | ||||
|       }); | ||||
|  | ||||
|       it('saves new challenge and syncs User', function(done) { | ||||
|         var challenge = specHelper.newChallenge(); | ||||
|         challenge.shortName = 'chal'; | ||||
|  | ||||
|         setTimeout(function() { | ||||
|           expect(User.sync).to.be.calledOnce; | ||||
|           done(); | ||||
|         }, 1000); | ||||
|  | ||||
|         scope.save(challenge); | ||||
|       }); | ||||
|  | ||||
|       it('saves new challenge and syncs User', function(done) { | ||||
|         sinon.stub(notification, 'text'); | ||||
|  | ||||
|         var challenge = specHelper.newChallenge(); | ||||
|         challenge.shortName = 'chal'; | ||||
|  | ||||
|         setTimeout(function() { | ||||
|           expect(notification.text).to.be.calledOnce; | ||||
|           expect(notification.text).to.be.calledWith(window.env.t('challengeCreated')); | ||||
|           done(); | ||||
|         }, 1000); | ||||
|  | ||||
|         scope.save(challenge); | ||||
|       }); | ||||
|     }); | ||||
|  | ||||
|     describe('create', function() { | ||||
|       it('creates new challenge with group that user has selected in filter', function() { | ||||
|         var party = specHelper.newGroup({ | ||||
|           type: 'party', | ||||
|           _id: 'user-party' | ||||
|         }); | ||||
|         scope.groupsFilter = [party]; | ||||
|         scope.search = { | ||||
|           group: { | ||||
|             'user-party': true | ||||
|           } | ||||
|         }; | ||||
|  | ||||
|         scope.create(); | ||||
|  | ||||
|         expect(scope.newChallenge.group).to.eql('user-party'); | ||||
|       }); | ||||
|  | ||||
|       it('uses first group in $scope.groups if more than one exists', function() { | ||||
|         var party = specHelper.newGroup({ | ||||
|           type: 'party', | ||||
|           _id: 'user-party' | ||||
|         }); | ||||
|         var guild = specHelper.newGroup({ | ||||
|           type: 'guild', | ||||
|           _id: 'guild' | ||||
|         }); | ||||
|         scope.groups = [party, guild]; | ||||
|         scope.groupsFilter = [party, guild]; | ||||
|         scope.search = { | ||||
|           group: { | ||||
|             'user-party': true, | ||||
|             'guild': true | ||||
|           } | ||||
|         }; | ||||
|  | ||||
|         scope.create(); | ||||
|  | ||||
|         expect(scope.newChallenge.group).to.eql('user-party'); | ||||
|       }); | ||||
|  | ||||
|       it('defaults to tavern if no group can be set as default', function() { | ||||
|         scope.create(); | ||||
|  | ||||
|         expect(scope.newChallenge.group).to.eql(tavernId); | ||||
|       }); | ||||
|  | ||||
|       it('calculates maxPrize', function() { | ||||
|         User.getBalanceInGems.returns(20); | ||||
|         scope.create(); | ||||
|  | ||||
|         expect(scope.maxPrize).to.eql(20); | ||||
|       }); | ||||
|  | ||||
|       it('sets newChallenge to a blank challenge', function() { | ||||
|         scope.create(); | ||||
|  | ||||
|         var chal = scope.newChallenge; | ||||
|  | ||||
|         expect(chal.name).to.eql(''); | ||||
|         expect(chal.description).to.eql(''); | ||||
|         expect(chal.habits).to.eql([]); | ||||
|         expect(chal.dailys).to.eql([]); | ||||
|         expect(chal.todos).to.eql([]); | ||||
|         expect(chal.rewards).to.eql([]); | ||||
|         expect(chal.leader).to.eql('unique-user-id'); | ||||
|         expect(chal.group).to.eql(tavernId); | ||||
|         expect(chal.timestamp).to.be.greaterThan(0); | ||||
|         expect(chal.official).to.eql(false); | ||||
|       }); | ||||
|     }); | ||||
|  | ||||
|     describe('insufficientGemsForTavernChallenge', function() { | ||||
|       context('tavern challenge', function() { | ||||
|         it('returns true if user has no gems', function() { | ||||
|           User.user.balance = 0; | ||||
|           scope.newChallenge = specHelper.newChallenge({ | ||||
|             group: tavernId | ||||
|           }); | ||||
|  | ||||
|           var cannotCreateTavernChallenge = scope.insufficientGemsForTavernChallenge(); | ||||
|           expect(cannotCreateTavernChallenge).to.eql(true); | ||||
|         }); | ||||
|  | ||||
|         it('returns false if user has gems', function() { | ||||
|           User.user.balance = .25; | ||||
|           scope.newChallenge = specHelper.newChallenge({ | ||||
|             group: tavernId | ||||
|           }); | ||||
|  | ||||
|           var cannotCreateTavernChallenge = scope.insufficientGemsForTavernChallenge(); | ||||
|           expect(cannotCreateTavernChallenge).to.eql(false); | ||||
|         }); | ||||
|       }); | ||||
|  | ||||
|       context('non-tavern challenge', function() { | ||||
|         it('returns false', function() { | ||||
|           User.user.balance = 0; | ||||
|           scope.newChallenge = specHelper.newChallenge({ | ||||
|             group: 'not-tavern' | ||||
|           }); | ||||
|  | ||||
|           var cannotCreateTavernChallenge = scope.insufficientGemsForTavernChallenge(); | ||||
|           expect(cannotCreateTavernChallenge).to.eql(false); | ||||
|         }); | ||||
|       }); | ||||
|     }); | ||||
|  | ||||
|     describe('edit', function() { | ||||
|       it('transitions to edit page', function() { | ||||
|         sandbox.stub(state, 'transitionTo'); | ||||
|         var challenge = specHelper.newChallenge({ | ||||
|           _id: 'challenge-id' | ||||
|         }); | ||||
|  | ||||
|         scope.edit(challenge); | ||||
|  | ||||
|         expect(state.transitionTo).to.be.calledOnce; | ||||
|         expect(state.transitionTo).to.be.calledWith( | ||||
|           'options.social.challenges.edit', | ||||
|           { cid: challenge._id }, | ||||
|           { reload: true, inherit: false, notify: true } | ||||
|         ); | ||||
|       }); | ||||
|     }); | ||||
|  | ||||
|     describe('discard', function() { | ||||
|       it('sets new challenge to null', function() { | ||||
|         scope.newChallenge = specHelper.newChallenge(); | ||||
|  | ||||
|         scope.discard(); | ||||
|  | ||||
|         expect(scope.newChallenge).to.not.exist; | ||||
|       }); | ||||
|     }); | ||||
|  | ||||
|     describe('clone', function() { | ||||
|  | ||||
|       var challengeToClone = { | ||||
|         name: 'copyChallenge', | ||||
|         description: 'copyChallenge', | ||||
|         habits: [specHelper.newHabit()], | ||||
|         dailys: [specHelper.newDaily()], | ||||
|         todos: [specHelper.newTodo()], | ||||
|         rewards: [specHelper.newReward()], | ||||
|         leader: 'unique-user-id', | ||||
|         group: { _id: "copyGroup" }, | ||||
|         timestamp: new Date("October 13, 2014 11:13:00"), | ||||
|         members: ['id', 'another-id'], | ||||
|         official: true, | ||||
|         _isMember: true, | ||||
|         prize: 1 | ||||
|       }; | ||||
|  | ||||
|       it('Clones the basic challenge info', function() { | ||||
|  | ||||
|         scope.clone(challengeToClone); | ||||
|  | ||||
|         expect(scope.newChallenge.name).to.eql(challengeToClone.name); | ||||
|         expect(scope.newChallenge.shortName).to.eql(challengeToClone.shortName); | ||||
|         expect(scope.newChallenge.description).to.eql(challengeToClone.description); | ||||
|         expect(scope.newChallenge.leader).to.eql(user._id); | ||||
|         expect(scope.newChallenge.group).to.eql(challengeToClone.group._id); | ||||
|         expect(scope.newChallenge.official).to.eql(challengeToClone.official); | ||||
|         expect(scope.newChallenge.prize).to.eql(challengeToClone.prize); | ||||
|       }); | ||||
|  | ||||
|       it('does not clone members', function() { | ||||
|         scope.clone(challengeToClone); | ||||
|  | ||||
|         expect(scope.newChallenge.members).to.not.exist; | ||||
|       }); | ||||
|  | ||||
|       it('does not clone timestamp', function() { | ||||
|         scope.clone(challengeToClone); | ||||
|  | ||||
|         expect(scope.newChallenge.timestamp).to.not.exist; | ||||
|       }); | ||||
|  | ||||
|       it('clones habits', function() { | ||||
|         scope.clone(challengeToClone); | ||||
|  | ||||
|         expect(scope.newChallenge.habits.length).to.eql(challengeToClone.habits.length); | ||||
|         expect(scope.newChallenge.habits[0].text).to.eql(challengeToClone.habits[0].text); | ||||
|         expect(scope.newChallenge.habits[0].notes).to.eql(challengeToClone.habits[0].notes); | ||||
|       }); | ||||
|  | ||||
|       it('clones dailys', function() { | ||||
|         scope.clone(challengeToClone); | ||||
|  | ||||
|         expect(scope.newChallenge.dailys.length).to.eql(challengeToClone.dailys.length); | ||||
|         expect(scope.newChallenge.dailys[0].text).to.eql(challengeToClone.dailys[0].text); | ||||
|         expect(scope.newChallenge.dailys[0].notes).to.eql(challengeToClone.dailys[0].notes); | ||||
|       }); | ||||
|  | ||||
|       it('clones todos', function() { | ||||
|         scope.clone(challengeToClone); | ||||
|  | ||||
|         expect(scope.newChallenge.todos.length).to.eql(challengeToClone.todos.length); | ||||
|         expect(scope.newChallenge.todos[0].text).to.eql(challengeToClone.todos[0].text); | ||||
|         expect(scope.newChallenge.todos[0].notes).to.eql(challengeToClone.todos[0].notes); | ||||
|       }); | ||||
|  | ||||
|       it('clones rewards', function() { | ||||
|         scope.clone(challengeToClone); | ||||
|  | ||||
|         expect(scope.newChallenge.rewards.length).to.eql(challengeToClone.rewards.length); | ||||
|         expect(scope.newChallenge.rewards[0].text).to.eql(challengeToClone.rewards[0].text); | ||||
|         expect(scope.newChallenge.rewards[0].notes).to.eql(challengeToClone.rewards[0].notes); | ||||
|       }); | ||||
|     }); | ||||
|   }); | ||||
|  | ||||
|   context('User interactions', function() { | ||||
|     describe('join', function() { | ||||
|       it('calls challenge join', function(){ | ||||
|         var joinChallengeSpy = sinon.spy(challenges, 'joinChallenge'); | ||||
|  | ||||
|         var challenge = specHelper.newChallenge({ | ||||
|           _id: 'challenge-to-join', | ||||
|         }); | ||||
|  | ||||
|         scope.join(challenge); | ||||
|  | ||||
|         expect(joinChallengeSpy).to.be.calledOnce; | ||||
|       }); | ||||
|     }); | ||||
|  | ||||
|     describe('clickLeave', function() { | ||||
|       var clickEvent = { | ||||
|         target: 'button' | ||||
|       }; | ||||
|  | ||||
|       it('sets selectedChal to passed in challenge', function() { | ||||
|         var challenge = specHelper.newChallenge({ | ||||
|           _id: 'popover-challenge-to-leave' | ||||
|         }); | ||||
|  | ||||
|         expect(scope.selectedChal).to.not.exist; | ||||
|  | ||||
|         scope.clickLeave(challenge, clickEvent); | ||||
|         expect(scope.selectedChal).to.eql(challenge); | ||||
|       }); | ||||
|  | ||||
|       it('creates popover element', function() { | ||||
|         var challenge = specHelper.newChallenge({ | ||||
|           _id: 'popover-challenge-to-leave' | ||||
|         }); | ||||
|  | ||||
|         expect(scope.popoverEl).to.not.exist; | ||||
|         scope.clickLeave(challenge, clickEvent); | ||||
|         expect(scope.popoverEl).to.exist; | ||||
|       }); | ||||
|     }); | ||||
|  | ||||
|     describe('leave', function() { | ||||
|       var challenge = specHelper.newChallenge({ | ||||
|         _id: 'challenge-to-leave', | ||||
|       }); | ||||
|  | ||||
|       var clickEvent = { | ||||
|         target: 'button' | ||||
|       }; | ||||
|  | ||||
|       it('removes selectedChal when cancel is chosen', function() { | ||||
|         scope.clickLeave(challenge, clickEvent); | ||||
|  | ||||
|         expect(scope.selectedChal).to.eql(challenge); | ||||
|  | ||||
|         scope.leave('cancel'); | ||||
|         expect(scope.selectedChal).to.not.exist; | ||||
|       }); | ||||
|  | ||||
|       it('calls challenge leave when anything but cancel is chosen', function() { | ||||
|         var leaveChallengeSpy = sinon.spy(challenges, 'leaveChallenge'); | ||||
|         scope.clickLeave(challenge, clickEvent); | ||||
|  | ||||
|         scope.leave('not-cancel', challenge); | ||||
|         expect(leaveChallengeSpy).to.be.calledOnce; | ||||
|       }); | ||||
|     }); | ||||
|   }); | ||||
|  | ||||
|   context('modal actions', function() { | ||||
|     beforeEach(function() { | ||||
|       sandbox.stub(members, 'selectMember'); | ||||
|       sandbox.stub(rootScope, 'openModal'); | ||||
|       members.selectMember.returns(Promise.resolve()); | ||||
|     }); | ||||
|  | ||||
|     describe('sendMessageToChallengeParticipant', function() { | ||||
|       it('opens private-message modal', function(done) { | ||||
|         scope.sendMessageToChallengeParticipant(user._id); | ||||
|  | ||||
|         setTimeout(function() { | ||||
|           expect(rootScope.openModal).to.be.calledOnce; | ||||
|           expect(rootScope.openModal).to.be.calledWith( | ||||
|             'private-message', | ||||
|             { controller: 'MemberModalCtrl' } | ||||
|           ); | ||||
|           done(); | ||||
|         }, 1000); | ||||
|       }); | ||||
|     }); | ||||
|  | ||||
|     describe('sendGiftToChallengeParticipant', function() { | ||||
|       it('opens send-gift modal', function(done) { | ||||
|         scope.sendGiftToChallengeParticipant(user._id); | ||||
|  | ||||
|         setTimeout(function() { | ||||
|           expect(rootScope.openModal).to.be.calledOnce; | ||||
|           expect(rootScope.openModal).to.be.calledWith( | ||||
|             'send-gift', | ||||
|             { controller: 'MemberModalCtrl' } | ||||
|           ); | ||||
|           done(); | ||||
|         }, 1000); | ||||
|       }); | ||||
|     }); | ||||
|   }); | ||||
| }); | ||||
| @@ -1,121 +0,0 @@ | ||||
| 'use strict'; | ||||
|  | ||||
| describe("Chat Controller", function() { | ||||
|   var scope, ctrl, user, $rootScope, $controller, $httpBackend, html; | ||||
|  | ||||
|   beforeEach(function() { | ||||
|     module(function($provide) { | ||||
|       $provide.value('User', {}); | ||||
|     }); | ||||
|  | ||||
|     inject(function(_$rootScope_, _$controller_, _$compile_, _$httpBackend_){ | ||||
|       user = specHelper.newUser(); | ||||
|       user._id = "unique-user-id"; | ||||
|       $rootScope = _$rootScope_; | ||||
|  | ||||
|       scope = _$rootScope_.$new(); | ||||
|  | ||||
|       $controller = _$controller_; | ||||
|       $httpBackend = _$httpBackend_; | ||||
|  | ||||
|       // Load RootCtrl to ensure shared behaviors are loaded | ||||
|       $controller('RootCtrl',  {$scope: scope, User: {user: user}}); | ||||
|  | ||||
|       html = _$compile_('<div><form ng-submit="postChat(group, message.content)"><textarea submit-on-meta-enter ng-model="message.content" ng-model-options="{debounce: 250}"></textarea></form></div>')(scope); | ||||
|       document.body.appendChild(html[0]); | ||||
|       ctrl = $controller('ChatCtrl', {$scope: scope, $element: html}); | ||||
|     }); | ||||
|   }); | ||||
|    | ||||
|   afterEach(function() { | ||||
|     html.remove(); | ||||
|   }); | ||||
|  | ||||
|   describe('copyToDo', function() { | ||||
|     it('when copying a user message it opens modal with information from message', function() { | ||||
|       scope.group = { | ||||
|         name: "Princess Bride" | ||||
|       }; | ||||
|  | ||||
|       var modalSpy = sandbox.spy($rootScope, "openModal"); | ||||
|       var message = { | ||||
|         uuid: 'the-dread-pirate-roberts', | ||||
|         user: 'Wesley', | ||||
|         text: 'As you wish' | ||||
|       }; | ||||
|  | ||||
|       scope.copyToDo(message); | ||||
|  | ||||
|       modalSpy.should.have.been.calledOnce; | ||||
|  | ||||
|       modalSpy.should.have.been.calledWith('copyChatToDo', sinon.match(function(callArgToMatch){ | ||||
|         return callArgToMatch.controller == 'CopyMessageModalCtrl' | ||||
|             && callArgToMatch.scope.text == message.text | ||||
|       })); | ||||
|     }); | ||||
|  | ||||
|     it('when copying a system message it opens modal with information from message', function() { | ||||
|       scope.group = { | ||||
|         name: "Princess Bride" | ||||
|       }; | ||||
|  | ||||
|       var modalSpy = sandbox.spy($rootScope, "openModal"); | ||||
|       var message = { | ||||
|         uuid: 'system', | ||||
|         text: 'Wesley attacked the ROUS in the Fire Swamp' | ||||
|       }; | ||||
|  | ||||
|       scope.copyToDo(message); | ||||
|  | ||||
|       modalSpy.should.have.been.calledOnce; | ||||
|  | ||||
|       modalSpy.should.have.been.calledWith('copyChatToDo', sinon.match(function(callArgToMatch){ | ||||
|         return callArgToMatch.controller == 'CopyMessageModalCtrl' | ||||
|             && callArgToMatch.scope.text == message.text | ||||
|       })); | ||||
|     }); | ||||
|   }); | ||||
|    | ||||
|   it('updates model on enter key press', function() { | ||||
|     // Set initial state of the page with some dummy data. | ||||
|     scope.group = { name: 'group' }; | ||||
|      | ||||
|     // The main controller is going to try to fetch the template right off.  | ||||
|     // No big deal, just return an empty string. | ||||
|     $httpBackend.when('GET', 'partials/main.html').respond(''); | ||||
|  | ||||
|     // Let the page settle, and the controllers set their initial state. | ||||
|     $rootScope.$digest(); | ||||
|  | ||||
|     // Watch for calls to postChat & make sure it doesn't do anything. | ||||
|     let postChatSpy = sandbox.stub(scope, 'postChat'); | ||||
|      | ||||
|     // Pretend we typed 'aaa' into the textarea. | ||||
|     var textarea = html.find('textarea');     | ||||
|     textarea[0].value = 'aaa'; | ||||
|     let inputEvent = new Event('input'); | ||||
|     textarea[0].dispatchEvent(inputEvent); | ||||
|  | ||||
|     // Give a change for the ng-model watchers to notice that the value in the | ||||
|     // textarea has changed. | ||||
|     $rootScope.$digest(); | ||||
|      | ||||
|     // Since no time has elapsed and we debounce the model change, we should  | ||||
|     // see no model update just yet. | ||||
|     expect(scope.message.content).to.equal(''); | ||||
|      | ||||
|     // Now, press the enter key in the textarea. We use jquery here to paper  | ||||
|     // over browser differences with initializing the keyboard event. | ||||
|     var keyboardEvent = jQuery.Event('keydown', {keyCode: 13, key: 'Enter', metaKey: true}); | ||||
|     jQuery(textarea).trigger(keyboardEvent); | ||||
|  | ||||
|     // Now, allow the model to update given the changes to the page still | ||||
|     // without letting any time elapse... | ||||
|     $rootScope.$digest(); | ||||
|      | ||||
|     // ... and nevertheless seeing the desired call to postChat with the right | ||||
|     // data. Yay! | ||||
|     postChatSpy.should.have.been.calledWith(scope.group, 'aaa'); | ||||
|   }); | ||||
| }); | ||||
|  | ||||
| @@ -1,59 +0,0 @@ | ||||
| 'use strict'; | ||||
|  | ||||
| describe("CopyMessageModal controller", function() { | ||||
|   var scope, ctrl, user, Notification, $rootScope, $controller; | ||||
|  | ||||
|   beforeEach(function() { | ||||
|     module(function($provide) { | ||||
|       var mockWindow = {href: '', alert: sandbox.spy(), location: {search: '', pathname: '', href: ''}}; | ||||
|  | ||||
|       $provide.value('$window', mockWindow); | ||||
|     }); | ||||
|  | ||||
|     inject(function($rootScope, _$controller_, _Notification_, User){ | ||||
|       user = specHelper.newUser(); | ||||
|       user._id = "unique-user-id"; | ||||
|       user.ops = { | ||||
|         addTask: sandbox.spy() | ||||
|       }; | ||||
|  | ||||
|       scope = $rootScope.$new(); | ||||
|       scope.$close = sandbox.spy(); | ||||
|  | ||||
|       $controller = _$controller_; | ||||
|  | ||||
|       User.setUser(user); | ||||
|  | ||||
|       // Load RootCtrl to ensure shared behaviors are loaded | ||||
|       $controller('RootCtrl',  {$scope: scope, User: User}); | ||||
|  | ||||
|       ctrl = $controller('CopyMessageModalCtrl', {$scope: scope, User: User}); | ||||
|  | ||||
|       Notification = _Notification_; | ||||
|       Notification.text = sandbox.spy(); | ||||
|     }); | ||||
|   }); | ||||
|  | ||||
|   describe("saveTodo", function() { | ||||
|     it('saves todo', function() { | ||||
|  | ||||
|       scope.text = "A Tavern msg"; | ||||
|       scope.notes = "Some notes"; | ||||
|       var payload = { | ||||
|         body: { | ||||
|           text: scope.text, | ||||
|           type: 'todo', | ||||
|           notes: scope.notes | ||||
|         } | ||||
|       }; | ||||
|  | ||||
|       scope.saveTodo(); | ||||
|  | ||||
|       user.ops.addTask.should.have.been.calledOnce; | ||||
|       user.ops.addTask.should.have.been.calledWith(payload); | ||||
|       Notification.text.should.have.been.calledOnce; | ||||
|       Notification.text.should.have.been.calledWith(window.env.t('messageAddedAsToDo')); | ||||
|       scope.$close.should.have.been.calledOnce; | ||||
|     }); | ||||
|   }); | ||||
| }); | ||||
| @@ -1,51 +0,0 @@ | ||||
| 'use strict'; | ||||
|  | ||||
| describe('Filters Controller', function() { | ||||
|   var scope, user, userService; | ||||
|  | ||||
|   beforeEach(function () { | ||||
|     module(function($provide) { | ||||
|       var mockWindow = {href: '', alert: sandbox.spy(), location: {search: '', pathname: '', href: ''}}; | ||||
|  | ||||
|       $provide.value('$window', mockWindow); | ||||
|     }); | ||||
|  | ||||
|     inject(function($rootScope, $controller, Shared, User) { | ||||
|       user = specHelper.newUser(); | ||||
|       Shared.wrap(user); | ||||
|       scope = $rootScope.$new(); | ||||
|       // user.filters = {}; | ||||
|       User.setUser(user); | ||||
|       User.user.filters = {}; | ||||
|       userService = User; | ||||
|       $controller('FiltersCtrl', {$scope: scope, User: User}); | ||||
|     }) | ||||
|   }); | ||||
|  | ||||
|   describe('tags', function(){ | ||||
|     it('creates a tag', function(){ | ||||
|       scope._newTag = {name:'tagName'} | ||||
|       scope.createTag(); | ||||
|       expect(user.tags).to.have.length(1); | ||||
|       expect(user.tags[0].name).to.eql('tagName'); | ||||
|       expect(user.tags[0]).to.have.property('id'); | ||||
|     }); | ||||
|  | ||||
|     it('toggles tag filtering', inject(function(Shared){ | ||||
|       var tag = {id: Shared.uuid(), name: 'myTag'}; | ||||
|       scope.toggleFilter(tag); | ||||
|       expect(userService.user.filters[tag.id]).to.eql(true); | ||||
|       scope.toggleFilter(tag); | ||||
|       expect(userService.user.filters[tag.id]).to.not.eql(true); | ||||
|     })); | ||||
|   }); | ||||
|  | ||||
|   describe('updateTaskFilter', function(){ | ||||
|     it('updatest user\'s filter query with the value of filterQuery', function () { | ||||
|       scope.filterQuery = 'task'; | ||||
|       scope.updateTaskFilter(); | ||||
|  | ||||
|       expect(userService.user.filterQuery).to.eql(scope.filterQuery); | ||||
|     }); | ||||
|   }); | ||||
| }); | ||||
| @@ -1,86 +0,0 @@ | ||||
| 'use strict'; | ||||
|  | ||||
| describe('Footer Controller', function() { | ||||
|   var scope, user, User; | ||||
|  | ||||
|   beforeEach(inject(function($rootScope, $controller) { | ||||
|     user = specHelper.newUser(); | ||||
|     User = { | ||||
|       log: sandbox.stub(), | ||||
|       set: sandbox.stub(), | ||||
|       addTenGems: sandbox.stub(), | ||||
|       addHourglass: sandbox.stub(), | ||||
|       user: user | ||||
|     }; | ||||
|     scope = $rootScope.$new(); | ||||
|     $controller('FooterCtrl', {$scope: scope, User: User, Social: {}}); | ||||
|   })); | ||||
|  | ||||
|   context('Debug mode', function() { | ||||
|     before(function() { | ||||
|       window.env.NODE_ENV = 'test'; | ||||
|     }); | ||||
|  | ||||
|     after(function() { | ||||
|       delete window.env.NODE_ENV; | ||||
|     }); | ||||
|  | ||||
|     describe('#setHealthLow', function(){ | ||||
|       it('sets user health to 1'); | ||||
|     }); | ||||
|  | ||||
|     describe('#addMissedDay', function(){ | ||||
|       beforeEach(function() { | ||||
|         sandbox.stub(confirm).returns(true); | ||||
|       }); | ||||
|  | ||||
|       it('Cancels if confirm box is not confirmed'); | ||||
|  | ||||
|       it('allows multiple days'); | ||||
|  | ||||
|       it('sets users last cron'); | ||||
|  | ||||
|       it('notifies uers'); | ||||
|     }); | ||||
|  | ||||
|     describe('#addTenGems', function() { | ||||
|       it('posts to /user/addTenGems', inject(function($httpBackend) { | ||||
|         scope.addTenGems(); | ||||
|  | ||||
|         expect(User.addTenGems).to.have.been.called; | ||||
|       })); | ||||
|     }); | ||||
|  | ||||
|     describe('#addHourglass', function() { | ||||
|       it('posts to /user/addHourglass', inject(function($httpBackend) { | ||||
|         scope.addHourglass(); | ||||
|  | ||||
|         expect(User.addHourglass).to.have.been.called; | ||||
|       })); | ||||
|     }); | ||||
|  | ||||
|     describe('#addGold', function() { | ||||
|       it('adds 500 gold to user'); | ||||
|     }); | ||||
|  | ||||
|     describe('#addMana', function() { | ||||
|       it('adds 500 mana to user'); | ||||
|     }); | ||||
|  | ||||
|     describe('#addLevelsAndGold', function() { | ||||
|       it('adds 10000 experience to user'); | ||||
|  | ||||
|       it('adds 10000 gp to user'); | ||||
|  | ||||
|       it('adds 10000 mp to user'); | ||||
|     }); | ||||
|  | ||||
|     describe('#addOneLevel', function() { | ||||
|       it('adds one level to user'); | ||||
|     }); | ||||
|  | ||||
|     describe('#addBossQuestProgressUp', function() { | ||||
|       it('adds 1000 progress to quest.progress.up'); | ||||
|     }); | ||||
|   }); | ||||
| }); | ||||
| @@ -1,279 +0,0 @@ | ||||
| 'use strict'; | ||||
|  | ||||
| describe('Groups Controller', function() { | ||||
|   var scope, ctrl, groups, user, guild, $rootScope; | ||||
|  | ||||
|   beforeEach(function() { | ||||
|     module(function($provide) { | ||||
|       $provide.value('User', {}); | ||||
|     }); | ||||
|  | ||||
|     inject(function($rootScope, $controller, Groups){ | ||||
|       user = specHelper.newUser(); | ||||
|       user._id = "unique-user-id"; | ||||
|  | ||||
|       scope = $rootScope.$new(); | ||||
|  | ||||
|       // Load RootCtrl to ensure shared behaviors are loaded | ||||
|       $controller('RootCtrl',  {$scope: scope, User: {user: user}}); | ||||
|  | ||||
|       ctrl = $controller('GroupsCtrl', {$scope: scope, User: {user: user}}); | ||||
|  | ||||
|       groups = Groups; | ||||
|     }); | ||||
|   }); | ||||
|  | ||||
|   describe("isMemberOfPendingQuest", function() { | ||||
|     var party; | ||||
|     var partyStub; | ||||
|  | ||||
|     beforeEach(function () { | ||||
|       party = specHelper.newGroup({ | ||||
|         _id: "unique-party-id", | ||||
|         type: 'party', | ||||
|         members: ['leader-id'] // Ensure we wouldn't pass automatically. | ||||
|       }); | ||||
|  | ||||
|       partyStub = sandbox.stub(groups, "party", function() { | ||||
|         return party; | ||||
|       }); | ||||
|     }); | ||||
|  | ||||
|     it("returns false if group is does not have a quest", function() { | ||||
|       expect(scope.isMemberOfPendingQuest(user._id, party)).to.not.be.ok; | ||||
|     }); | ||||
|  | ||||
|     it("returns false if group quest has not members", function() { | ||||
|       party.quest = { | ||||
|         'key': 'random-key', | ||||
|       }; | ||||
|       expect(scope.isMemberOfPendingQuest(user._id, party)).to.not.be.ok; | ||||
|     }); | ||||
|  | ||||
|     it("returns false if group quest is active", function() { | ||||
|       party.quest = { | ||||
|         'key': 'random-key', | ||||
|         'members': {}, | ||||
|         'active': true, | ||||
|       }; | ||||
|       party.quest.members[user._id] = true; | ||||
|       expect(scope.isMemberOfPendingQuest(user._id, party)).to.not.be.ok; | ||||
|     }); | ||||
|  | ||||
|     it("returns true if user is a member of a pending quest", function() { | ||||
|       party.quest = { | ||||
|         'key': 'random-key', | ||||
|         'members': {}, | ||||
|       }; | ||||
|       party.quest.members[user._id] = true; | ||||
|       expect(scope.isMemberOfPendingQuest(user._id, party)).to.be.ok; | ||||
|     }); | ||||
|   }); | ||||
|  | ||||
|   describe("isMemberOfGroup", function() { | ||||
|     it("returns true if group is the user's party retrieved from groups service", function() { | ||||
|       var party = specHelper.newGroup({ | ||||
|         _id: "unique-party-id", | ||||
|         type: 'party', | ||||
|         members: ['leader-id'] // Ensure we wouldn't pass automatically. | ||||
|       }); | ||||
|  | ||||
|       var partyStub = sandbox.stub(groups, "party", function() { | ||||
|         return party; | ||||
|       }); | ||||
|  | ||||
|       expect(scope.isMemberOfGroup(user._id, party)).to.be.ok; | ||||
|     }); | ||||
|  | ||||
|     it('returns true if guild is included in myGuilds call', function(){ | ||||
|  | ||||
|       var guild = specHelper.newGroup({ | ||||
|         _id: "unique-guild-id", | ||||
|         type: 'guild', | ||||
|         members: [user._id] | ||||
|       }); | ||||
|  | ||||
|       user.guilds = [guild._id]; | ||||
|  | ||||
|       expect(scope.isMemberOfGroup(user._id, guild)).to.be.ok; | ||||
|     }); | ||||
|  | ||||
|     it('does not return true if guild is not included in myGuilds call', function(){ | ||||
|  | ||||
|       var guild = specHelper.newGroup({ | ||||
|         _id: "unique-guild-id", | ||||
|         type: 'guild', | ||||
|         members: ['not-user-id'] | ||||
|       }); | ||||
|  | ||||
|       user.guilds = []; | ||||
|  | ||||
|       expect(scope.isMemberOfGroup(user._id, guild)).to.not.be.ok; | ||||
|     }); | ||||
|   }); | ||||
|  | ||||
|   describe('isAbleToEditGroup', () => { | ||||
|     var guild; | ||||
|  | ||||
|     beforeEach(() => { | ||||
|       user.contributor = {}; | ||||
|       guild = specHelper.newGroup({ | ||||
|         _id: 'unique-guild-id', | ||||
|         type: 'guild', | ||||
|         members: ['not-user-id'], | ||||
|         $save: sandbox.spy(), | ||||
|       }); | ||||
|     }); | ||||
|  | ||||
|     it('returns true if user is an admin', () => { | ||||
|       guild.leader = 'not-user-id'; | ||||
|       user.contributor.admin = true; | ||||
|       expect(scope.isAbleToEditGroup(guild)).to.be.ok; | ||||
|     }); | ||||
|  | ||||
|     it('returns true if user is group leader', () => { | ||||
|       guild.leader = {_id: user._id} | ||||
|       expect(scope.isAbleToEditGroup(guild)).to.be.ok; | ||||
|     }); | ||||
|  | ||||
|     it('returns false is user is not a leader or admin', () => { | ||||
|       expect(scope.isAbleToEditGroup(guild)).to.not.be.ok; | ||||
|     }); | ||||
|  | ||||
|     it('returns false is user is an admin but group is a party', () => { | ||||
|       guild.type = 'party'; | ||||
|       user.contributor.admin = true; | ||||
|       expect(scope.isAbleToEditGroup(guild)).to.not.be.ok; | ||||
|     }); | ||||
|   }); | ||||
|  | ||||
|   describe('editGroup', () => { | ||||
|     var guild; | ||||
|  | ||||
|     beforeEach(() => { | ||||
|       guild = specHelper.newGroup({ | ||||
|         _id: 'unique-guild-id', | ||||
|         leader: 'old leader', | ||||
|         type: 'guild', | ||||
|         members: ['not-user-id'], | ||||
|         $save: sandbox.spy(), | ||||
|       }); | ||||
|     }); | ||||
|  | ||||
|     it('marks group as being in edit mode', () => { | ||||
|       scope.editGroup(guild); | ||||
|  | ||||
|       expect(guild._editing).to.eql(true); | ||||
|     }); | ||||
|  | ||||
|     it('copies group to groupCopy', () => { | ||||
|       scope.editGroup(guild); | ||||
|  | ||||
|       for (var key in scope.groupCopy) { | ||||
|         expect(scope.groupCopy[key]).to.eql(guild[key]); | ||||
|       } | ||||
|     }); | ||||
|  | ||||
|     it('does not change original group when groupCopy is changed', () => { | ||||
|       scope.editGroup(guild); | ||||
|  | ||||
|       scope.groupCopy.leader = 'new leader'; | ||||
|       expect(scope.groupCopy.leader).to.not.eql(guild.leader); | ||||
|     }); | ||||
|   }); | ||||
|  | ||||
|   describe('saveEdit', () => { | ||||
|     let guild; | ||||
|  | ||||
|     beforeEach(() => { | ||||
|       guild = specHelper.newGroup({ | ||||
|         _id: 'unique-guild-id', | ||||
|         name: 'old name', | ||||
|         leader: 'old leader', | ||||
|         type: 'guild', | ||||
|         members: ['not-user-id'], | ||||
|         $save: () => {}, | ||||
|       }); | ||||
|  | ||||
|       scope.editGroup(guild); | ||||
|     }); | ||||
|  | ||||
|     it('calls group update', () => { | ||||
|       let guildUpdate = sandbox.spy(groups.Group, 'update'); | ||||
|  | ||||
|       scope.saveEdit(guild); | ||||
|  | ||||
|       expect(guildUpdate).to.be.calledOnce; | ||||
|     }); | ||||
|  | ||||
|     it('calls cancelEdit', () => { | ||||
|       sandbox.stub(scope, 'cancelEdit'); | ||||
|  | ||||
|       scope.saveEdit(guild); | ||||
|  | ||||
|       expect(scope.cancelEdit).to.be.calledOnce; | ||||
|     }); | ||||
|  | ||||
|     it('applies changes to groupCopy to original group', () => { | ||||
|       scope.groupCopy.name = 'new name'; | ||||
|  | ||||
|       scope.saveEdit(guild); | ||||
|  | ||||
|       expect(guild.name).to.eql('new name'); | ||||
|     }); | ||||
|  | ||||
|     it('assigns leader id to group if leader has changed', () => { | ||||
|       scope.groupCopy._newLeader = { _id: 'some leader id' }; | ||||
|  | ||||
|       scope.saveEdit(guild); | ||||
|  | ||||
|       expect(guild.leader).to.eql('some leader id'); | ||||
|     }); | ||||
|  | ||||
|     it('does not assign new leader id if leader object is not passed in', () => { | ||||
|       scope.groupCopy._newLeader = 'not an object'; | ||||
|  | ||||
|       scope.saveEdit(guild); | ||||
|  | ||||
|       expect(guild.leader).to.eql('old leader'); | ||||
|     }); | ||||
|   }); | ||||
|  | ||||
|   describe('cancelEdit', () => { | ||||
|     beforeEach(() => { | ||||
|       guild = specHelper.newGroup({ | ||||
|         _id: 'unique-guild-id', | ||||
|         name: 'old name', | ||||
|         leader: 'old leader', | ||||
|         type: 'guild', | ||||
|         members: ['not-user-id'], | ||||
|         $save: () => {}, | ||||
|       }); | ||||
|  | ||||
|       scope.editGroup(guild); | ||||
|     }); | ||||
|  | ||||
|     it('sets _editing to false on group', () => { | ||||
|       expect(guild._editing).to.eql(true); | ||||
|  | ||||
|       scope.cancelEdit(guild); | ||||
|  | ||||
|       expect(guild._editing).to.eql(false); | ||||
|     }); | ||||
|  | ||||
|     it('reset groupCopy to an empty object', () => { | ||||
|       expect(scope.groupCopy).to.not.eql({}); | ||||
|  | ||||
|       scope.cancelEdit(guild); | ||||
|  | ||||
|       expect(scope.groupCopy).to.eql({}); | ||||
|     }); | ||||
|   }); | ||||
|  | ||||
|   /* TODO: Modal testing */ | ||||
|   describe.skip("deleteAllMessages", function() { }); | ||||
|   describe.skip("clickMember", function() { }); | ||||
|   describe.skip("removeMember", function() { }); | ||||
|   describe.skip("confirmRemoveMember", function() { }); | ||||
|   describe.skip("quickReply", function() { }); | ||||
| }); | ||||
| @@ -1,63 +0,0 @@ | ||||
| describe('Group Tasks Meta Actions Controller', () => { | ||||
|   let rootScope, scope, user, userSerivce; | ||||
|  | ||||
|   beforeEach(() => { | ||||
|     module(function($provide) { | ||||
|       $provide.value('User', {}); | ||||
|     }); | ||||
|  | ||||
|     inject(($rootScope, $controller) => { | ||||
|       rootScope = $rootScope; | ||||
|  | ||||
|       user = specHelper.newUser(); | ||||
|       user._id = "unique-user-id"; | ||||
|       userSerivce = {user: user}; | ||||
|  | ||||
|       scope = $rootScope.$new(); | ||||
|  | ||||
|       scope.task = { | ||||
|         group: { | ||||
|           assignedUsers: [], | ||||
|           approval: { | ||||
|             required: false, | ||||
|           } | ||||
|         }, | ||||
|       }; | ||||
|       scope.task._edit = angular.copy(scope.task); | ||||
|  | ||||
|       $controller('GroupTaskActionsCtrl', {$scope: scope, User: userSerivce}); | ||||
|     }); | ||||
|   }); | ||||
|  | ||||
|   describe('toggleTaskRequiresApproval', function () { | ||||
|     it('toggles task approval required field from false to true', function () { | ||||
|       scope.toggleTaskRequiresApproval(); | ||||
|       expect(scope.task._edit.group.approval.required).to.be.true; | ||||
|     }); | ||||
|  | ||||
|     it('toggles task approval required field from true to false', function () { | ||||
|       scope.task._edit.group.approval.required = true; | ||||
|       scope.toggleTaskRequiresApproval(); | ||||
|       expect(scope.task._edit.group.approval.required).to.be.false; | ||||
|     }); | ||||
|   }); | ||||
|  | ||||
|  | ||||
|   describe('assign events', function () { | ||||
|     it('adds a group member to assigned users on "addedGroupMember" event ', () => { | ||||
|       var testId = 'test-id'; | ||||
|       rootScope.$broadcast('addedGroupMember', testId); | ||||
|       expect(scope.task.group.assignedUsers).to.contain(testId); | ||||
|       expect(scope.task._edit.group.assignedUsers).to.contain(testId); | ||||
|     }); | ||||
|  | ||||
|     it('removes a group member to assigned users on "addedGroupMember" event ', () => { | ||||
|       var testId = 'test-id'; | ||||
|       scope.task.group.assignedUsers.push(testId); | ||||
|       scope.task._edit.group.assignedUsers.push(testId); | ||||
|       rootScope.$broadcast('removedGroupMember', testId); | ||||
|       expect(scope.task.group.assignedUsers).to.not.contain(testId); | ||||
|       expect(scope.task._edit.group.assignedUsers).to.not.contain(testId); | ||||
|     }); | ||||
|   }); | ||||
| }); | ||||
| @@ -1,42 +0,0 @@ | ||||
| describe('Group Task Actions Controller', () => { | ||||
|   let scope, user, userSerivce; | ||||
|  | ||||
|   beforeEach(() => { | ||||
|     module(function($provide) { | ||||
|       $provide.value('User', {}); | ||||
|     }); | ||||
|  | ||||
|     inject(($rootScope, $controller) => { | ||||
|       user = specHelper.newUser(); | ||||
|       user._id = "unique-user-id"; | ||||
|       userSerivce = {user: user}; | ||||
|       userSerivce.sync = sandbox.stub(); | ||||
|  | ||||
|       scope = $rootScope.$new(); | ||||
|  | ||||
|       $controller('GroupTaskMetaActionsCtrl', {$scope: scope, User: userSerivce}); | ||||
|  | ||||
|       scope.task = { | ||||
|         group: { | ||||
|           assignedUsers: [], | ||||
|         }, | ||||
|       }; | ||||
|     }); | ||||
|   }); | ||||
|  | ||||
|   describe('claim', () => { | ||||
|     beforeEach(() => { | ||||
|       sandbox.stub(window, 'confirm').returns(true); | ||||
|     }); | ||||
|  | ||||
|     it('adds user to assigned users of scope task ', () => { | ||||
|       scope.claim(); | ||||
|       expect(scope.task.group.assignedUsers).to.contain(user._id); | ||||
|     }); | ||||
|  | ||||
|     it('syncs user tasks ', () => { | ||||
|       scope.claim(); | ||||
|       expect(userSerivce.sync).to.be.calledOnce; | ||||
|     }); | ||||
|   }); | ||||
| }); | ||||
| @@ -1,32 +0,0 @@ | ||||
| 'use strict'; | ||||
|  | ||||
| describe('Hall of Heroes Controller', function() { | ||||
|   var scope, ctrl, user, $rootScope; | ||||
|  | ||||
|   beforeEach(function() { | ||||
|     module(function($provide) { | ||||
|       $provide.value('User', {}); | ||||
|     }); | ||||
|  | ||||
|     inject(function($rootScope, $controller){ | ||||
|       user = specHelper.newUser(); | ||||
|  | ||||
|       scope = $rootScope.$new(); | ||||
|  | ||||
|       // Load RootCtrl to ensure shared behaviors are loaded | ||||
|       $controller('RootCtrl',  {$scope: scope, User: {user: user}}); | ||||
|  | ||||
|       ctrl = $controller('HallHeroesCtrl', {$scope: scope, User: {user: user}}); | ||||
|     }); | ||||
|   }); | ||||
|  | ||||
|   it('populates contributor input with selected hero id', function(){ | ||||
|     var loadHero = sandbox.spy(scope, "loadHero"); | ||||
|     var scrollTo = sandbox.spy(window, "scrollTo"); | ||||
|  | ||||
|     scope.populateContributorInput(user._id); | ||||
|     expect(scope._heroID).to.eql(user._id); | ||||
|     expect(loadHero.callCount).to.eql(1); | ||||
|     expect(scrollTo.callCount).to.eql(1); | ||||
|   }); | ||||
| }); | ||||
| @@ -1,50 +0,0 @@ | ||||
| 'use strict'; | ||||
|  | ||||
| describe('Header Controller', function() { | ||||
|   var scope, ctrl, user, $location, $rootScope; | ||||
|  | ||||
|   beforeEach(function() { | ||||
|     module(function($provide) { | ||||
|       user = specHelper.newUser(); | ||||
|       user._id = "unique-user-id" | ||||
|       $provide.value('User', {user: user}); | ||||
|     }); | ||||
|  | ||||
|     inject(function(_$rootScope_, _$controller_, _$location_){ | ||||
|       scope = _$rootScope_.$new(); | ||||
|       $rootScope = _$rootScope_; | ||||
|  | ||||
|       $location = _$location_; | ||||
|  | ||||
|       // Load RootCtrl to ensure shared behaviors are loaded | ||||
|       _$controller_('RootCtrl',  {$scope: scope, User: {user: user}}); | ||||
|  | ||||
|       ctrl = _$controller_('HeaderCtrl', {$scope: scope, User: {user: user}}); | ||||
|     }); | ||||
|   }); | ||||
|  | ||||
|   context('inviteOrStartParty', function(){ | ||||
|     beforeEach(function(){ | ||||
|       sandbox.stub($location, 'path'); | ||||
|       sandbox.stub($rootScope, 'openModal'); | ||||
|     }); | ||||
|  | ||||
|     it('redirects to party page if user does not have a party', function(){ | ||||
|       var group = {}; | ||||
|       scope.inviteOrStartParty(group); | ||||
|  | ||||
|       expect($location.path).to.be.calledWith("/options/groups/party"); | ||||
|       expect($rootScope.openModal).to.not.be.called; | ||||
|     }); | ||||
|  | ||||
|     it('Opens invite-friends modal if user has a party', function(){ | ||||
|       var group = { | ||||
|         type: 'party' | ||||
|       }; | ||||
|       scope.inviteOrStartParty(group); | ||||
|  | ||||
|       expect($rootScope.openModal).to.be.calledOnce; | ||||
|       expect($location.path).to.not.be.called; | ||||
|     }); | ||||
|   }); | ||||
| }); | ||||
| @@ -1,73 +0,0 @@ | ||||
| 'use strict'; | ||||
|  | ||||
| describe('inbox Controller', function() { | ||||
|   var scope, ctrl, user, $rootScope, $controller; | ||||
|  | ||||
|   beforeEach(function() { | ||||
|     module(function($provide) { | ||||
|       $provide.value('User', {}); | ||||
|     }); | ||||
|  | ||||
|     inject(function(_$rootScope_, _$controller_){ | ||||
|       user = specHelper.newUser(); | ||||
|       user._id = 'unique-user-id'; | ||||
|       $rootScope = _$rootScope_; | ||||
|  | ||||
|       scope = _$rootScope_.$new(); | ||||
|  | ||||
|       $controller = _$controller_; | ||||
|  | ||||
|       // Load RootCtrl to ensure shared behaviors are loaded | ||||
|       $controller('RootCtrl',  {$scope: scope, User: {user: user}}); | ||||
|  | ||||
|       ctrl = $controller('InboxCtrl', {$scope: scope}); | ||||
|     }); | ||||
|   }); | ||||
|  | ||||
|   describe('copyToDo', function() { | ||||
|     it('when copying a user message it opens modal with information from message', function() { | ||||
|       scope.group = { | ||||
|         name: 'Princess Bride' | ||||
|       }; | ||||
|  | ||||
|       sandbox.spy($rootScope, 'openModal'); | ||||
|       var message = { | ||||
|         uuid: 'the-dread-pirate-roberts', | ||||
|         user: 'Wesley', | ||||
|         text: 'As you wish' | ||||
|       }; | ||||
|  | ||||
|       scope.copyToDo(message); | ||||
|  | ||||
|       expect($rootScope.openModal).to.be.calledOnce; | ||||
|       expect($rootScope.openModal).to.be.calledWith('copyChatToDo', sinon.match(function(callArgToMatch){ | ||||
|         var taskText = env.t('taskTextFromInbox', { | ||||
|           from: message.user | ||||
|         }); | ||||
|         return callArgToMatch.controller == 'CopyMessageModalCtrl' | ||||
|         && callArgToMatch.scope.text == taskText | ||||
|       })); | ||||
|     }); | ||||
|  | ||||
|     it('when copying a system message it opens modal with information from message', function() { | ||||
|  | ||||
|       var modalSpy = sandbox.spy($rootScope, 'openModal'); | ||||
|       var message = { | ||||
|         uuid: 'system', | ||||
|         text: 'Wesley attacked the ROUS in the Fire Swamp' | ||||
|       }; | ||||
|  | ||||
|       scope.copyToDo(message); | ||||
|  | ||||
|       modalSpy.should.have.been.calledOnce; | ||||
|  | ||||
|       modalSpy.should.have.been.calledWith('copyChatToDo', sinon.match(function(callArgToMatch){ | ||||
|         var taskText = env.t('taskTextFromInbox', { | ||||
|           from: 'system' | ||||
|         }); | ||||
|         return callArgToMatch.controller == 'CopyMessageModalCtrl' | ||||
|         && callArgToMatch.scope.text == taskText | ||||
|       })); | ||||
|     }); | ||||
|   }); | ||||
| }); | ||||
| @@ -1,538 +0,0 @@ | ||||
| 'use strict'; | ||||
|  | ||||
| describe('Inventory Controller', function() { | ||||
|   var scope, ctrl, user, rootScope, shared, achievement; | ||||
|  | ||||
|   beforeEach(function() { | ||||
|     module(function($provide) { | ||||
|       var mockWindow = { | ||||
|         confirm: function(msg) { | ||||
|           return true; | ||||
|         }, | ||||
|         location: {search: '', pathname: '', href: ''}, | ||||
|       }; | ||||
|  | ||||
|       $provide.value('$window', mockWindow); | ||||
|     }); | ||||
|  | ||||
|     inject(function($rootScope, $controller, Shared, User, $location, $window, Achievement) { | ||||
|       user = specHelper.newUser({ | ||||
|         balance: 4, | ||||
|         items: { | ||||
|           gear: { owned: {} }, | ||||
|           eggs: { Cactus: 1 }, | ||||
|           hatchingPotions: { Base: 1 }, | ||||
|           food: { Meat: 1 }, | ||||
|           pets: {}, | ||||
|           mounts: {} | ||||
|         }, | ||||
|         preferences: { | ||||
|           suppressModals: {} | ||||
|         }, | ||||
|         purchased: { | ||||
|           plan: { | ||||
|             mysteryItems: [], | ||||
|           }, | ||||
|         }, | ||||
|       }); | ||||
|  | ||||
|       Shared.wrap(user); | ||||
|       shared = Shared; | ||||
|       achievement = Achievement; | ||||
|  | ||||
|       scope = $rootScope.$new(); | ||||
|       rootScope = $rootScope; | ||||
|  | ||||
|       User.user = user; | ||||
|       User.setUser(user); | ||||
|  | ||||
|       // Load RootCtrl to ensure shared behaviors are loaded | ||||
|       $controller('RootCtrl',  {$scope: scope, User: User}); | ||||
|  | ||||
|       ctrl = $controller('InventoryCtrl', {$scope: scope, User: User}); | ||||
|     }); | ||||
|   }); | ||||
|  | ||||
|   it('starts without any item selected', function(){ | ||||
|     expect(scope.selectedEgg).to.eql(null); | ||||
|     expect(scope.selectedPotion).to.eql(null); | ||||
|     expect(scope.selectedFood).to.eql(undefined); | ||||
|   }); | ||||
|  | ||||
|   it('chooses an egg', function(){ | ||||
|     scope.chooseEgg('Cactus'); | ||||
|     expect(scope.selectedEgg.key).to.eql('Cactus'); | ||||
|   }); | ||||
|  | ||||
|   it('chooses a potion', function(){ | ||||
|     scope.choosePotion('Base'); | ||||
|     expect(scope.selectedPotion.key).to.eql('Base'); | ||||
|   }); | ||||
|  | ||||
|   describe('Hatching Pets', function(){ | ||||
|     beforeEach(function() { | ||||
|       sandbox.stub(rootScope, 'openModal'); | ||||
|     }); | ||||
|  | ||||
|     it('hatches a pet', function(){ | ||||
|       scope.chooseEgg('Cactus'); | ||||
|       scope.choosePotion('Base'); | ||||
|       expect(user.items.eggs).to.eql({Cactus: 0}); | ||||
|       expect(user.items.hatchingPotions).to.eql({Base: 0}); | ||||
|       expect(user.items.pets).to.eql({'Cactus-Base': 5}); | ||||
|       expect(scope.selectedEgg).to.eql(null); | ||||
|       expect(scope.selectedPotion).to.eql(null); | ||||
|     }); | ||||
|  | ||||
|     it('shows a modal for pet hatching', function(){ | ||||
|       scope.chooseEgg('Cactus'); | ||||
|       scope.choosePotion('Base'); | ||||
|  | ||||
|       expect(rootScope.openModal).to.have.been.calledOnce; | ||||
|       expect(rootScope.openModal).to.have.been.calledWith('hatchPet'); | ||||
|     }); | ||||
|  | ||||
|     it('shows modal even if user has raised that pet to a mount', function(){ | ||||
|       user.items.pets['Cactus-Base'] = -1; | ||||
|       scope.chooseEgg('Cactus'); | ||||
|       scope.choosePotion('Base'); | ||||
|  | ||||
|       expect(rootScope.openModal).to.have.been.calledOnce; | ||||
|       expect(rootScope.openModal).to.have.been.calledWith('hatchPet'); | ||||
|     }); | ||||
|  | ||||
|     //@TODO: Fix Common hatch | ||||
|     xit('does not show modal if user tries to hatch a pet they own', function(){ | ||||
|       user.items.pets['Cactus-Base'] = 5; | ||||
|       scope.chooseEgg('Cactus'); | ||||
|       scope.choosePotion('Base'); | ||||
|       expect(rootScope.openModal).to.not.have.been.called; | ||||
|     }); | ||||
|  | ||||
|     //@TODO: Fix Common hatch | ||||
|     xit('does not show modal if user tries to hatch a premium quest pet', function(){ | ||||
|       user.items.eggs = {Snake: 1}; | ||||
|       user.items.hatchingPotions = {Peppermint: 1}; | ||||
|       scope.chooseEgg('Snake'); | ||||
|       scope.choosePotion('Peppermint'); | ||||
|       expect(rootScope.openModal).to.not.have.been.called; | ||||
|     }); | ||||
|  | ||||
|     it('does not show pet hatching modal if user has opted out', function(){ | ||||
|       user.preferences.suppressModals.hatchPet = true; | ||||
|       scope.chooseEgg('Cactus'); | ||||
|       scope.choosePotion('Base'); | ||||
|  | ||||
|       expect(rootScope.openModal).to.not.be.called; | ||||
|     }); | ||||
|  | ||||
|     it('shows beastMaster achievement modal if user has all 90 pets', function(){ | ||||
|       sandbox.stub(achievement, 'displayAchievement'); | ||||
|       sandbox.stub(shared.count, "beastMasterProgress").returns(90); | ||||
|       scope.chooseEgg('Cactus'); | ||||
|       scope.choosePotion('Base'); | ||||
|  | ||||
|       expect(achievement.displayAchievement).to.be.called; | ||||
|       expect(achievement.displayAchievement).to.be.calledWith('beastMaster'); | ||||
|     }); | ||||
|  | ||||
|     it('shows triadBingo achievement modal if user has all pets twice and all mounts', function(){ | ||||
|       sandbox.stub(achievement, 'displayAchievement'); | ||||
|       sandbox.stub(shared.count, "mountMasterProgress").returns(90); | ||||
|       sandbox.stub(shared.count, "dropPetsCurrentlyOwned").returns(90); | ||||
|       scope.chooseEgg('Cactus'); | ||||
|       scope.choosePotion('Base'); | ||||
|  | ||||
|       expect(achievement.displayAchievement).to.be.called; | ||||
|       expect(achievement.displayAchievement).to.be.calledWith('triadBingo'); | ||||
|     }); | ||||
|   }); | ||||
|  | ||||
|   describe('Feeding and Raising Pets', function() { | ||||
|     beforeEach(function() { | ||||
|       sandbox.stub(rootScope, 'openModal'); | ||||
|       user.items.pets = {'PandaCub-Base':5}; | ||||
|       user.items.mounts = {'PandaCub-Base':false}; | ||||
|     }); | ||||
|  | ||||
|     it('feeds a pet', function() { | ||||
|       scope.chooseFood('Meat'); | ||||
|       scope.choosePet('PandaCub','Base'); | ||||
|  | ||||
|       expect(user.items.pets['PandaCub-Base']).to.eql(10); | ||||
|     }); | ||||
|  | ||||
|     it('gives weaker benefit when feeding inappropriate food', function() { | ||||
|       user.items.food.Honey = 1; | ||||
|  | ||||
|       scope.chooseFood('Honey'); | ||||
|       scope.choosePet('PandaCub','Base'); | ||||
|  | ||||
|       expect(user.items.pets['PandaCub-Base']).to.eql(7); | ||||
|     }); | ||||
|  | ||||
|     it('raises pet to a mount when feeding gauge maxes out', function() { | ||||
|       user.items.pets['PandaCub-Base'] = 45; | ||||
|  | ||||
|       scope.chooseFood('Meat'); | ||||
|       scope.choosePet('PandaCub','Base'); | ||||
|  | ||||
|       expect(user.items.pets['PandaCub-Base']).to.eql(-1); | ||||
|       expect(user.items.mounts['PandaCub-Base']).to.exist; | ||||
|     }); | ||||
|  | ||||
|     it('raises pet to a mount instantly when using a Saddle', function() { | ||||
|       user.items.food.Saddle = 1; | ||||
|  | ||||
|       scope.chooseFood('Saddle'); | ||||
|       scope.choosePet('PandaCub','Base'); | ||||
|  | ||||
|       expect(user.items.pets['PandaCub-Base']).to.eql(-1); | ||||
|       expect(user.items.mounts['PandaCub-Base']).to.exist; | ||||
|     }); | ||||
|  | ||||
|     it('displays mount raising modal for drop pets', function() { | ||||
|       user.items.food.Saddle = 1; | ||||
|  | ||||
|       scope.chooseFood('Saddle'); | ||||
|       scope.choosePet('PandaCub','Base'); | ||||
|  | ||||
|       expect(rootScope.openModal).to.have.been.calledOnce; | ||||
|       expect(rootScope.openModal).to.have.been.calledWith('raisePet'); | ||||
|     }); | ||||
|  | ||||
|     it('displays mount raising modal for quest pets', function() { | ||||
|       user.items.food.Saddle = 1; | ||||
|       user.items.pets['Snake-Base'] = 1; | ||||
|  | ||||
|       scope.chooseFood('Saddle'); | ||||
|       scope.choosePet('Snake','Base'); | ||||
|  | ||||
|       expect(rootScope.openModal).to.have.been.calledOnce; | ||||
|       expect(rootScope.openModal).to.have.been.calledWith('raisePet'); | ||||
|     }); | ||||
|  | ||||
|     it('displays mount raising modal for premium pets', function() { | ||||
|       user.items.food.Saddle = 1; | ||||
|       user.items.pets['TigerCub-Spooky'] = 1; | ||||
|  | ||||
|       scope.chooseFood('Saddle'); | ||||
|       scope.choosePet('TigerCub','Spooky'); | ||||
|  | ||||
|       expect(rootScope.openModal).to.have.been.calledOnce; | ||||
|       expect(rootScope.openModal).to.have.been.calledWith('raisePet'); | ||||
|     }); | ||||
|  | ||||
|     it('shows mountMaster achievement modal if user has all 90 mounts', function(){ | ||||
|       sandbox.stub(achievement, 'displayAchievement'); | ||||
|       sandbox.stub(shared.count, "mountMasterProgress").returns(90); | ||||
|       scope.chooseFood('Meat'); | ||||
|       scope.choosePet('PandaCub','Base'); | ||||
|  | ||||
|       expect(achievement.displayAchievement).to.be.calledOnce; | ||||
|       expect(achievement.displayAchievement).to.be.calledWith('mountMaster'); | ||||
|     }); | ||||
|   }); | ||||
|  | ||||
|   it('sells an egg', function(){ | ||||
|     scope.chooseEgg('Cactus'); | ||||
|     scope.sellInventory(); | ||||
|     expect(user.items.eggs).to.eql({Cactus: 0}); | ||||
|     expect(user.stats.gp).to.eql(3); | ||||
|   }); | ||||
|  | ||||
|   it('sells a potion', function(){ | ||||
|     scope.choosePotion('Base'); | ||||
|     scope.sellInventory(); | ||||
|     expect(user.items.hatchingPotions).to.eql({Base: 0}); | ||||
|     expect(user.stats.gp).to.eql(2); | ||||
|   }); | ||||
|  | ||||
|   it('sells food', function(){ | ||||
|     scope.chooseFood('Meat'); | ||||
|     scope.sellInventory(); | ||||
|     expect(user.items.food).to.eql({Meat: 0}); | ||||
|     expect(user.stats.gp).to.eql(1); | ||||
|   }); | ||||
|  | ||||
|   it('chooses a pet', function(){ | ||||
|     user.items.pets['Cactus-Base'] = 5; | ||||
|     scope.choosePet('Cactus', 'Base'); | ||||
|     expect(user.items.currentPet).to.eql('Cactus-Base'); | ||||
|   }); | ||||
|  | ||||
|   it('purchases an egg', inject(function(Content){ | ||||
|     scope.purchase('eggs', Content.eggs['Wolf']); | ||||
|     expect(user.balance).to.eql(3.25); | ||||
|     expect(user.items.eggs).to.eql({Cactus: 1, Wolf: 1}) | ||||
|   })); | ||||
|  | ||||
|   describe('Deselecting Items', function() { | ||||
|     it('deselects a food', function(){ | ||||
|       scope.chooseFood('Meat'); | ||||
|       scope.deselectItem(); | ||||
|       expect(scope.selectedFood).to.eql(null); | ||||
|     }); | ||||
|  | ||||
|     it('deselects a potion', function(){ | ||||
|       scope.choosePotion('Base'); | ||||
|       scope.deselectItem(); | ||||
|       expect(scope.selectedPotion).to.eql(null); | ||||
|     }); | ||||
|  | ||||
|     it('deselects a egg', function(){ | ||||
|       scope.chooseEgg('Cactus'); | ||||
|       scope.deselectItem(); | ||||
|       expect(scope.selectedEgg).to.eql(null); | ||||
|     }); | ||||
|   }); | ||||
|  | ||||
|   describe('openCardsModal', function(type, numberOfVariations) { | ||||
|     var cardsModalScope; | ||||
|  | ||||
|     beforeEach(function() { | ||||
|       cardsModalScope = {}; | ||||
|       sandbox.stub(rootScope, 'openModal'); | ||||
|       sandbox.stub(rootScope, '$new').returns(cardsModalScope); | ||||
|     }); | ||||
|  | ||||
|     it('opens cards modal', function() { | ||||
|       scope.openCardsModal('valentine', 4); | ||||
|  | ||||
|       expect(rootScope.openModal).to.be.calledOnce; | ||||
|       expect(rootScope.openModal).to.be.calledWith( | ||||
|         'cards' | ||||
|       ); | ||||
|     }); | ||||
|  | ||||
|     it('instantiates a new scope for the modal', function() { | ||||
|       scope.openCardsModal('valentine', 4); | ||||
|  | ||||
|       expect(rootScope.$new).to.be.calledOnce; | ||||
|       expect(cardsModalScope.cardType).to.eql('valentine'); | ||||
|       expect(cardsModalScope.cardMessage).to.exist; | ||||
|     }); | ||||
|  | ||||
|     it('provides a card message', function() { | ||||
|       scope.openCardsModal('valentine', 1); | ||||
|  | ||||
|       expect(cardsModalScope.cardMessage).to.eql(env.t('valentine0')); | ||||
|     }); | ||||
|  | ||||
|     it('randomly generates message from x number of messages', function() { | ||||
|       var possibleValues = [env.t('valentine0'), env.t('valentine1')]; | ||||
|  | ||||
|       scope.openCardsModal('valentine', 2); | ||||
|  | ||||
|       expect(possibleValues).to.contain(cardsModalScope.cardMessage); | ||||
|     }); | ||||
|   }); | ||||
|  | ||||
|   describe('#buyQuest', function() { | ||||
|     var quests, questObject; | ||||
|  | ||||
|     beforeEach(inject(function(Quests) { | ||||
|       quests = Quests; | ||||
|       questObject = { key: 'whale' }; | ||||
|  | ||||
|       sandbox.stub(quests, 'buyQuest').returns({ then: function(res) { res(questObject); } }); | ||||
|     })); | ||||
|  | ||||
|     it('calls Quests.buyQuest', function() { | ||||
|       scope.buyQuest('foo'); | ||||
|  | ||||
|       expect(quests.buyQuest).to.be.calledOnce; | ||||
|       expect(quests.buyQuest).to.be.calledWith('foo'); | ||||
|     }); | ||||
|  | ||||
|     it('sets selectedQuest to resolved quest object', function() { | ||||
|       scope.buyQuest('whale'); | ||||
|  | ||||
|       expect(rootScope.selectedQuest).to.eql(questObject); | ||||
|     }); | ||||
|  | ||||
|     it('opens buyQuest modal', function() { | ||||
|       sandbox.spy(rootScope, 'openModal'); | ||||
|  | ||||
|       scope.buyQuest('whale'); | ||||
|  | ||||
|       expect(rootScope.openModal).to.be.calledOnce; | ||||
|       expect(rootScope.openModal).to.be.calledWith('buyQuest', {controller: 'InventoryCtrl'}); | ||||
|     }); | ||||
|   }); | ||||
|  | ||||
|   describe('#showQuest', function() { | ||||
|     var quests, questObject; | ||||
|  | ||||
|     beforeEach(inject(function(Quests) { | ||||
|       quests = Quests; | ||||
|       questObject = { key: 'whale' }; | ||||
|  | ||||
|       sandbox.stub(quests, 'showQuest').returns({ then: function(res) { res(questObject); } }); | ||||
|     })); | ||||
|  | ||||
|     it('calls Quests.showQuest', function() { | ||||
|       scope.showQuest('foo'); | ||||
|  | ||||
|       expect(quests.showQuest).to.be.calledOnce; | ||||
|       expect(quests.showQuest).to.be.calledWith('foo'); | ||||
|     }); | ||||
|  | ||||
|     it('sets selectedQuest to resolved quest object', function() { | ||||
|       scope.showQuest('whale'); | ||||
|  | ||||
|       expect(rootScope.selectedQuest).to.eql(questObject); | ||||
|     }); | ||||
|  | ||||
|     it('opens showQuest modal', function() { | ||||
|       sandbox.spy(rootScope, 'openModal'); | ||||
|  | ||||
|       scope.showQuest('whale'); | ||||
|  | ||||
|       expect(rootScope.openModal).to.be.calledOnce; | ||||
|       expect(rootScope.openModal).to.be.calledWith('showQuest', {controller: 'InventoryCtrl'}); | ||||
|     }); | ||||
|   }); | ||||
|  | ||||
|   describe('#hasAllTimeTravelerItems', function() { | ||||
|     it('returns false if items remain for purchase with Mystic Hourglasses', function() { | ||||
|       expect(scope.hasAllTimeTravelerItems()).to.eql(false); | ||||
|     }); | ||||
|  | ||||
|     it('returns true if there are no items left to purchase', inject(function(Content) { | ||||
|       _.forEach(Content.gear.flat, function(v,item) { | ||||
|         if (item.indexOf('mystery') > -1) { | ||||
|           user.items.gear.owned[item] = true; | ||||
|         } | ||||
|       }); | ||||
|       _.forEach(Content.timeTravelStable.pets, function(v,pet) { | ||||
|         user.items.pets[pet] = 5; | ||||
|       }); | ||||
|       _.forEach(Content.timeTravelStable.mounts, function(v,mount) { | ||||
|         user.items.mounts[mount] = true; | ||||
|       }); | ||||
|  | ||||
|       expect(scope.hasAllTimeTravelerItems()).to.eql(true); | ||||
|     })); | ||||
|   }); | ||||
|  | ||||
|   describe('#hasAllTimeTravelerItemsOfType', function() { | ||||
|     it('returns false for Mystery Sets if there are sets left in the time traveler store', function() { | ||||
|       expect(scope.hasAllTimeTravelerItemsOfType('mystery')).to.eql(false); | ||||
|     }); | ||||
|  | ||||
|     it('returns true for Mystery Sets if there are no sets left to purchase', inject(function(Content) { | ||||
|       _.forEach(Content.gear.flat, function(v,item) { | ||||
|         if (item.indexOf('mystery') > -1) { | ||||
|           user.items.gear.owned[item] = true; | ||||
|         } | ||||
|       }); | ||||
|  | ||||
|       expect(scope.hasAllTimeTravelerItemsOfType('mystery')).to.eql(true); | ||||
|     })); | ||||
|  | ||||
|     it('returns false for pets if user does not own all pets in the Time Travel Stable', function() { | ||||
|       expect(scope.hasAllTimeTravelerItemsOfType('pets')).to.eql(false); | ||||
|     }); | ||||
|  | ||||
|     it('returns true for pets if user owns all pets in the Time Travel Stable', inject(function(Content) { | ||||
|       _.forEach(Content.timeTravelStable.pets, function(v,pet) { | ||||
|         user.items.pets[pet] = 5; | ||||
|       }); | ||||
|  | ||||
|       expect(scope.hasAllTimeTravelerItemsOfType('pets')).to.eql(true); | ||||
|     })); | ||||
|  | ||||
|     it('returns false for mounts if user does not own all mounts in the Time Travel Stable', function() { | ||||
|       expect(scope.hasAllTimeTravelerItemsOfType('mounts')).to.eql(false); | ||||
|     }); | ||||
|  | ||||
|     it('returns true for mounts if user owns all mounts in the Time Travel Stable', inject(function(Content) { | ||||
|       _.forEach(Content.timeTravelStable.mounts, function(v,mount) { | ||||
|         user.items.mounts[mount] = true; | ||||
|       }); | ||||
|  | ||||
|       expect(scope.hasAllTimeTravelerItemsOfType('mounts')).to.eql(true); | ||||
|     })); | ||||
|   }); | ||||
|  | ||||
|   describe('Gear search filter', function() { | ||||
|     var wrap = function(text) { | ||||
|       return {'text': function() {return text;}}; | ||||
|     } | ||||
|  | ||||
|     var toText = function(list) { | ||||
|       return _.map(list, function(ele) { return ele.text(); }); | ||||
|     } | ||||
|  | ||||
|     var gearByClass, gearByType; | ||||
|  | ||||
|     beforeEach(function() { | ||||
|       scope.$digest(); | ||||
|       gearByClass = {'raw': [wrap('kale'), wrap('sashimi')], | ||||
|                      'cooked': [wrap('chicken'), wrap('potato')]}; | ||||
|  | ||||
|       gearByType = {'veg': [wrap('kale'), wrap('potato')], | ||||
|                     'not': [wrap('chicken'), wrap('sashimi')]}; | ||||
|       scope.gearByClass = gearByClass; | ||||
|       scope.gearByType = gearByType; | ||||
|       scope.equipmentQuery.query = 'a'; | ||||
|     }); | ||||
|  | ||||
|     it('filters nothing if equipmentQuery is nothing', function() { | ||||
|       scope.equipmentQuery.query = ''; | ||||
|       scope.$digest(); | ||||
|       expect(toText(scope.filteredGearByClass['raw'])).to.eql(['kale', 'sashimi']); | ||||
|       expect(toText(scope.filteredGearByClass['cooked'])).to.eql(['chicken', 'potato']); | ||||
|       expect(toText(scope.filteredGearByType['veg'])).to.eql(['kale', 'potato']); | ||||
|       expect(toText(scope.filteredGearByType['not'])).to.eql(['chicken', 'sashimi']); | ||||
|     }); | ||||
|  | ||||
|     it('filters out gear if class gear changes', function() { | ||||
|       scope.$digest(); | ||||
|       expect(toText(scope.filteredGearByClass['raw'])).to.eql(['kale', 'sashimi']); | ||||
|       expect(toText(scope.filteredGearByClass['cooked'])).to.eql(['potato']); | ||||
|  | ||||
|       scope.gearByClass['raw'].push(wrap('zucchini')); | ||||
|       scope.gearByClass['cooked'].push(wrap('pizza')); | ||||
|       scope.$digest(); | ||||
|       expect(toText(scope.filteredGearByClass['raw'])).to.eql(['kale', 'sashimi']); | ||||
|       expect(toText(scope.filteredGearByClass['cooked'])).to.eql(['potato', 'pizza']); | ||||
|     }); | ||||
|  | ||||
|     it('filters out gear if typed gear changes', function() { | ||||
|       scope.$digest(); | ||||
|       expect(toText(scope.filteredGearByType['veg'])).to.eql(['kale', 'potato']); | ||||
|       expect(toText(scope.filteredGearByType['not'])).to.eql(['sashimi']); | ||||
|  | ||||
|       scope.gearByType['veg'].push(wrap('zucchini')); | ||||
|       scope.gearByType['not'].push(wrap('pizza')); | ||||
|  | ||||
|       scope.$digest(); | ||||
|       expect(toText(scope.filteredGearByType['veg'])).to.eql(['kale', 'potato']); | ||||
|       expect(toText(scope.filteredGearByType['not'])).to.eql(['sashimi', 'pizza']); | ||||
|     }); | ||||
|  | ||||
|     it('filters out gear if filter query changes', function() { | ||||
|       scope.equipmentQuery.query = 'c'; | ||||
|       scope.$digest(); | ||||
|  | ||||
|       expect(toText(scope.filteredGearByClass['raw'])).to.eql([]); | ||||
|       expect(toText(scope.filteredGearByClass['cooked'])).to.eql(['chicken']); | ||||
|       expect(toText(scope.filteredGearByType['veg'])).to.eql([]); | ||||
|       expect(toText(scope.filteredGearByType['not'])).to.eql(['chicken']); | ||||
|     }); | ||||
|  | ||||
|     it('returns the right filtered gear', function() { | ||||
|       var equipment = [wrap('spicy tuna'), wrap('dragon'), wrap('rainbow'), wrap('caterpillar')]; | ||||
|       expect(toText(scope.equipmentSearch(equipment, 'ra'))).to.eql(['dragon', 'rainbow']); | ||||
|     }); | ||||
|  | ||||
|     it('returns the right filtered gear if the source gear has unicode', function() { | ||||
|       // blue hat, red hat, red shield | ||||
|       var equipment = [wrap('藍色軟帽'), wrap('紅色軟帽'), wrap('紅色盾牌')]; | ||||
|       // searching for 'red' gives red hat, red shield | ||||
|       expect(toText(scope.equipmentSearch(equipment, '紅色'))).to.eql(['紅色軟帽', '紅色盾牌']); | ||||
|     }); | ||||
|   }); | ||||
| }); | ||||
| @@ -1,257 +0,0 @@ | ||||
| 'use strict'; | ||||
|  | ||||
| describe('Invite to Group Controller', function() { | ||||
|   var scope, ctrl, groups, user, guild, rootScope, $controller; | ||||
|  | ||||
|   beforeEach(function() { | ||||
|     user = specHelper.newUser({ | ||||
|       profile: { name: 'Mario' } | ||||
|     }); | ||||
|  | ||||
|     module(function($provide) { | ||||
|       $provide.value('User', {}); | ||||
|       $provide.value('injectedGroup', { user: user }); | ||||
|     }); | ||||
|  | ||||
|     inject(function(_$rootScope_, _$controller_, Groups) { | ||||
|       rootScope = _$rootScope_; | ||||
|  | ||||
|       scope = _$rootScope_.$new(); | ||||
|  | ||||
|       $controller = _$controller_; | ||||
|  | ||||
|       // Load RootCtrl to ensure shared behaviors are loaded | ||||
|       $controller('RootCtrl',  {$scope: scope, User: {user: user}}); | ||||
|  | ||||
|       ctrl = $controller('InviteToGroupCtrl', {$scope: scope, User: {user: user}}); | ||||
|  | ||||
|       groups = Groups; | ||||
|     }); | ||||
|   }); | ||||
|  | ||||
|   describe('addEmail', function() { | ||||
|     it('adds blank email to email list', function() { | ||||
|       scope.emails = [{name: 'Mario', email: 'mario@mushroomkingdom.com'}]; | ||||
|       scope.addEmail(); | ||||
|  | ||||
|       expect(scope.emails).to.eql([{name: 'Mario', email: 'mario@mushroomkingdom.com'}, {name: '', email: ''}]); | ||||
|     }); | ||||
|   }); | ||||
|  | ||||
|   describe('addUuid', function() { | ||||
|     it('adds blank uuid to invitees list', function() { | ||||
|       scope.invitees = [{uuid: 'user1'}]; | ||||
|       scope.addUuid(); | ||||
|  | ||||
|       expect(scope.invitees).to.eql([{uuid: 'user1'}, {uuid: ''}]); | ||||
|     }); | ||||
|   }); | ||||
|  | ||||
|   describe('inviteNewUsers', function() { | ||||
|     var groupInvite, groupCreate; | ||||
|  | ||||
|     beforeEach(function() { | ||||
|       scope.group = specHelper.newGroup({ | ||||
|         type: 'party', | ||||
|       }); | ||||
|  | ||||
|       groupCreate = sandbox.stub(groups.Group, 'create'); | ||||
|       groupInvite = sandbox.stub(groups.Group, 'invite'); | ||||
|     }); | ||||
|  | ||||
|     context('if the party does not already exist', function() { | ||||
|       var groupResponse; | ||||
|  | ||||
|       beforeEach(function() { | ||||
|         delete scope.group._id; | ||||
|         groupResponse = {data: {data: scope.group}} | ||||
|       }); | ||||
|  | ||||
|       it('saves the group if a new group is being created', function() { | ||||
|         groupCreate.returns(Promise.resolve(groupResponse)); | ||||
|         scope.inviteNewUsers('uuid'); | ||||
|         expect(groupCreate).to.be.calledOnce; | ||||
|       }); | ||||
|  | ||||
|       it('uses provided name', function() { | ||||
|         scope.group.name = 'test party'; | ||||
|  | ||||
|         groupCreate.returns(Promise.resolve(groupResponse)); | ||||
|  | ||||
|         scope.inviteNewUsers('uuid'); | ||||
|  | ||||
|         expect(groupCreate).to.be.calledWith(scope.group); | ||||
|         expect(scope.group.name).to.eql('test party'); | ||||
|       }); | ||||
|  | ||||
|       it('names the group if no name is provided', function() { | ||||
|         scope.group.name = ''; | ||||
|  | ||||
|         groupCreate.returns(Promise.resolve(groupResponse)); | ||||
|  | ||||
|         scope.inviteNewUsers('uuid'); | ||||
|  | ||||
|         expect(groupCreate).to.be.calledWith(scope.group); | ||||
|         expect(scope.group.name).to.eql(env.t('possessiveParty', {name: user.profile.name})); | ||||
|       }); | ||||
|     }); | ||||
|  | ||||
|     context('email', function() { | ||||
|       beforeEach(function () { | ||||
|         sandbox.stub(rootScope, 'hardRedirect'); | ||||
|       }); | ||||
|  | ||||
|       it('invites user with emails', function(done) { | ||||
|         scope.emails = [ | ||||
|           {name: 'Luigi', email: 'mario_bro@themushroomkingdom.com'}, | ||||
|           {name: 'Mario', email: 'mario@tmk.com'} | ||||
|         ]; | ||||
|  | ||||
|         var inviteDetails = { | ||||
|           inviter: user.profile.name, | ||||
|           emails: [ | ||||
|             {name: 'Luigi', email: 'mario_bro@themushroomkingdom.com'}, | ||||
|             {name: 'Mario', email: 'mario@tmk.com'} | ||||
|           ] | ||||
|         }; | ||||
|  | ||||
|         groupInvite.returns( | ||||
|           Promise.resolve() | ||||
|             .then(function () { | ||||
|               expect(groupInvite).to.be.calledOnce; | ||||
|               expect(groupInvite).to.be.calledWith(scope.group._id, inviteDetails); | ||||
|               done(); | ||||
|             }) | ||||
|         ); | ||||
|  | ||||
|         scope.inviteNewUsers('email'); | ||||
|       }); | ||||
|  | ||||
|       it('resets email list after sending', function(done) { | ||||
|         scope.emails[0].name = 'Luigi'; | ||||
|         scope.emails[0].email = 'mario_bro@themushroomkingdom.com'; | ||||
|  | ||||
|         groupInvite.returns( | ||||
|           Promise.resolve() | ||||
|             .then(function () { | ||||
|               //We use a timeout to test items that happen after the promise is resolved | ||||
|               setTimeout(function(){ | ||||
|                 expect(scope.emails).to.eql([{name:'', email: ''},{name:'', email: ''}]); | ||||
|                 done(); | ||||
|               }, 1000); | ||||
|             }) | ||||
|         ); | ||||
|  | ||||
|         scope.inviteNewUsers('email'); | ||||
|       }); | ||||
|  | ||||
|       it('filters out blank email inputs', function() { | ||||
|         scope.emails = [ | ||||
|           {name: 'Luigi', email: 'mario_bro@themushroomkingdom.com'}, | ||||
|           {name: 'Toad', email: ''}, | ||||
|           {name: 'Mario', email: 'mario@tmk.com'} | ||||
|         ]; | ||||
|  | ||||
|         var inviteDetails = { | ||||
|           inviter: user.profile.name, | ||||
|           emails: [ | ||||
|             {name: 'Luigi', email: 'mario_bro@themushroomkingdom.com'}, | ||||
|             {name: 'Mario', email: 'mario@tmk.com'} | ||||
|           ] | ||||
|         }; | ||||
|  | ||||
|         groupInvite.returns( | ||||
|           Promise.resolve() | ||||
|             .then(function () { | ||||
|               expect(groupInvite).to.be.calledOnce; | ||||
|               expect(groupInvite).to.be.calledWith(scope.group._id, inviteDetails); | ||||
|               done(); | ||||
|             }) | ||||
|         ); | ||||
|  | ||||
|         scope.inviteNewUsers('email'); | ||||
|       }); | ||||
|     }); | ||||
|  | ||||
|     context('uuid', function() { | ||||
|       beforeEach(function () { | ||||
|         sandbox.stub(rootScope, 'hardRedirect'); | ||||
|       }); | ||||
|  | ||||
|       it('invites user with uuid', function(done) { | ||||
|         scope.invitees = [{uuid: '1234'}]; | ||||
|  | ||||
|         groupInvite.returns( | ||||
|           Promise.resolve() | ||||
|             .then(function () { | ||||
|               expect(groupInvite).to.be.calledOnce; | ||||
|               expect(groupInvite).to.be.calledWith(scope.group._id, { uuids: ['1234'] }); | ||||
|               done(); | ||||
|             }) | ||||
|         ); | ||||
|  | ||||
|         scope.inviteNewUsers('uuid'); | ||||
|       }); | ||||
|  | ||||
|       it('invites users with uuids', function(done) { | ||||
|         scope.invitees = [{uuid: 'user1'}, {uuid: 'user2'}, {uuid: 'user3'}]; | ||||
|  | ||||
|         groupInvite.returns( | ||||
|           Promise.resolve() | ||||
|             .then(function () { | ||||
|               expect(groupInvite).to.be.calledOnce; | ||||
|               expect(groupInvite).to.be.calledWith(scope.group._id, { uuids: ['user1', 'user2', 'user3'] }); | ||||
|               done(); | ||||
|             }) | ||||
|         ); | ||||
|  | ||||
|         scope.inviteNewUsers('uuid'); | ||||
|       }); | ||||
|  | ||||
|       it('resets invitee list after sending', function(done) { | ||||
|         scope.invitees = [{uuid: 'user1'}, {uuid: 'user2'}, {uuid: 'user3'}]; | ||||
|  | ||||
|         groupInvite.returns( | ||||
|           Promise.resolve() | ||||
|             .then(function () { | ||||
|               //We use a timeout to test items that happen after the promise is resolved | ||||
|               setTimeout(function(){ | ||||
|                 expect(scope.invitees).to.eql([{uuid: ''}]); | ||||
|                 done(); | ||||
|               }, 1000); | ||||
|               done(); | ||||
|             }) | ||||
|         ); | ||||
|  | ||||
|         scope.inviteNewUsers('uuid'); | ||||
|       }); | ||||
|  | ||||
|       it('removes blank fields from being sent', function() { | ||||
|         scope.invitees = [{uuid: 'user1'}, {uuid: ''}, {uuid: 'user3'}]; | ||||
|  | ||||
|         groupInvite.returns( | ||||
|           Promise.resolve() | ||||
|             .then(function () { | ||||
|               expect(groupInvite).to.be.calledOnce; | ||||
|               expect(groupInvite).to.be.calledWith(scope.group._id, { uuids: ['user1', 'user3'] }); | ||||
|               done(); | ||||
|             }) | ||||
|         ); | ||||
|  | ||||
|         scope.inviteNewUsers('uuid'); | ||||
|       }); | ||||
|     }); | ||||
|  | ||||
|     context('invalid invite method', function() { | ||||
|       it('logs error', function() { | ||||
|         sandbox.stub(console, 'log'); | ||||
|  | ||||
|         scope.inviteNewUsers(); | ||||
|         expect(groups.Group.invite).to.not.be.called; | ||||
|         expect(console.log).to.be.calledOnce; | ||||
|         expect(console.log).to.be.calledWith('Invalid invite method.'); | ||||
|       }); | ||||
|     }); | ||||
|  | ||||
|   }); | ||||
| }); | ||||
| @@ -1,26 +0,0 @@ | ||||
| 'use strict'; | ||||
|  | ||||
| describe('Menu Controller', function() { | ||||
|  | ||||
|   describe('MenuCtrl', function(){ | ||||
|     var scope, ctrl, user, $httpBackend, $window; | ||||
|  | ||||
|     beforeEach(function(){ | ||||
|       module(function($provide) { | ||||
|         $provide.value('Chat', { seenMessage: function() {} }); | ||||
|       }); | ||||
|  | ||||
|       inject(function(_$httpBackend_, $rootScope, $controller) { | ||||
|         scope = $rootScope.$new(); | ||||
|  | ||||
|         ctrl = $controller('MenuCtrl', {$scope: scope, $window: $window, User: user}); | ||||
|       }) | ||||
|     }); | ||||
|  | ||||
|     describe('clearMessage', function() { | ||||
|       it('is Chat.seenMessage', inject(function(Chat) { | ||||
|         expect(scope.clearMessages).to.eql(Chat.markChatSeen); | ||||
|       })); | ||||
|     }); | ||||
|   }); | ||||
| }); | ||||
| @@ -1,191 +0,0 @@ | ||||
| 'use strict'; | ||||
|  | ||||
| describe('Notification Controller', function() { | ||||
|   var user, scope, rootScope, fakeBackend, achievement, ctrl; | ||||
|  | ||||
|   beforeEach(function() { | ||||
|     user = specHelper.newUser(); | ||||
|     user._id = "unique-user-id"; | ||||
|     user.needsCron = false; | ||||
|  | ||||
|     var userSync = sinon.stub().returns({ | ||||
|       then: function then (f) { f(); } | ||||
|     }); | ||||
|  | ||||
|     let User = { | ||||
|       user, | ||||
|       readNotification: function noop () {}, | ||||
|       readNotifications: function noop () {}, | ||||
|       sync: userSync | ||||
|     }; | ||||
|  | ||||
|     module(function($provide) { | ||||
|       $provide.value('User', User); | ||||
|       $provide.value('Guide', {}); | ||||
|     }); | ||||
|  | ||||
|     inject(function(_$rootScope_, $httpBackend, _$controller_, Achievement, Shared) { | ||||
|       scope = _$rootScope_.$new(); | ||||
|       rootScope = _$rootScope_; | ||||
|  | ||||
|       fakeBackend = $httpBackend; | ||||
|       fakeBackend.when('GET', 'partials/main.html').respond({}); | ||||
|  | ||||
|       achievement = Achievement; | ||||
|  | ||||
|       Shared.wrap(user); | ||||
|  | ||||
|       // Load RootCtrl to ensure shared behaviors are loaded | ||||
|       _$controller_('RootCtrl',  {$scope: scope, User}); | ||||
|  | ||||
|       ctrl = _$controller_('NotificationCtrl', {$scope: scope, User}); | ||||
|     }); | ||||
|  | ||||
|     sandbox.stub(rootScope, 'openModal'); | ||||
|     sandbox.stub(achievement, 'displayAchievement'); | ||||
|   }); | ||||
|  | ||||
|   describe('Quest Invitation modal watch', function() { | ||||
|     it('opens quest invitation modal', function() { | ||||
|       user.party.quest.RSVPNeeded = true; | ||||
|       delete user.party.quest.completed; | ||||
|       scope.$digest(); | ||||
|  | ||||
|       expect(rootScope.openModal).to.be.calledOnce; | ||||
|       expect(rootScope.openModal).to.be.calledWith('questInvitation', {controller:'PartyCtrl'}); | ||||
|     }); | ||||
|  | ||||
|     it('does not open quest invitation modal if RSVPNeeded is not true', function() { | ||||
|       user.party.quest.RSVPNeeded = false; | ||||
|       delete user.party.quest.completed; | ||||
|       scope.$digest(); | ||||
|  | ||||
|       expect(rootScope.openModal).to.not.be.called; | ||||
|     }); | ||||
|  | ||||
|     it('does not open quest invitation modal if quest.completed contains a quest key', function() { | ||||
|       user.party.quest.RSVPNeeded = true; | ||||
|       user.party.quest.completed = "hedgebeast"; | ||||
|       scope.$digest(); | ||||
|  | ||||
|       expect(rootScope.openModal).to.not.be.calledWith('questInvitation', {controller:'PartyCtrl'}); | ||||
|     }); | ||||
|   }); | ||||
|  | ||||
|   describe('Quest Completion modal watch', function() { | ||||
|     it('opens quest completion modal', function() { | ||||
|       user.party.quest.completed = "hedgebeast"; | ||||
|       scope.$digest(); | ||||
|  | ||||
|       expect(rootScope.openModal).to.be.calledOnce; | ||||
|       expect(rootScope.openModal).to.be.calledWith('questCompleted', {controller:'InventoryCtrl'}); | ||||
|     }); | ||||
|  | ||||
|     // Ensures that the completion modal opens before the invitation modal | ||||
|     it('opens quest completion modal if RSVPNeeded is true', function() { | ||||
|       user.party.quest.RSVPNeeded = true; | ||||
|       user.party.quest.completed = "hedgebeast"; | ||||
|       scope.$digest(); | ||||
|  | ||||
|       expect(rootScope.openModal).to.be.calledOnce; | ||||
|       expect(rootScope.openModal).to.be.calledWith('questCompleted', {controller:'InventoryCtrl'}); | ||||
|     }); | ||||
|  | ||||
|     it('does not open quest completion modal if quest.completed is null', function() { | ||||
|       user.party.quest.completed = null; | ||||
|       scope.$digest(); | ||||
|  | ||||
|       expect(rootScope.openModal).to.not.be.called; | ||||
|     }); | ||||
|   }); | ||||
|  | ||||
|   describe('User challenge won notification watch', function() { | ||||
|     it('opens challenge won modal when a challenge-won notification is received', function() { | ||||
|       rootScope.$digest(); | ||||
|       rootScope.userNotifications.push({type: 'WON_CHALLENGE'}); | ||||
|       rootScope.$digest(); | ||||
|  | ||||
|       expect(achievement.displayAchievement).to.be.called; | ||||
|       expect(achievement.displayAchievement).to.be.calledWith('wonChallenge'); | ||||
|     }); | ||||
|  | ||||
|     it('does not open challenge won modal if no new challenge-won notification is received', function() { | ||||
|       rootScope.$digest(); | ||||
|       rootScope.$digest(); | ||||
|  | ||||
|       expect(achievement.displayAchievement).to.not.be.calledWith('wonChallenge'); | ||||
|     }); | ||||
|   }); | ||||
|  | ||||
|   describe('User streak achievement notification watch', function() { | ||||
|     it('opens streak achievement modal when a streak-achievement notification is received', function() { | ||||
|       rootScope.$digest(); | ||||
|       rootScope.userNotifications.push({type: 'STREAK_ACHIEVEMENT'}); | ||||
|       rootScope.$digest(); | ||||
|  | ||||
|       expect(achievement.displayAchievement).to.be.called; | ||||
|       expect(achievement.displayAchievement).to.be.calledWith('streak', {size: 'md'}); | ||||
|     }); | ||||
|  | ||||
|     it('does not open streak achievement modal if no new streak-achievement notification is received', function() { | ||||
|       rootScope.$digest(); | ||||
|       rootScope.$digest(); | ||||
|  | ||||
|       expect(achievement.displayAchievement).to.not.be.calledWith('streak', {size: 'md'}); | ||||
|     }); | ||||
|   }); | ||||
|  | ||||
|   describe('User ultimate gear set achievement notification watch', function() { | ||||
|     it('opens ultimate gear set achievement modal when an ultimate-gear-achievement notification is received', function() { | ||||
|       rootScope.$digest(); | ||||
|       rootScope.userNotifications.push({type: 'ULTIMATE_GEAR_ACHIEVEMENT'}); | ||||
|       rootScope.$digest(); | ||||
|  | ||||
|       expect(achievement.displayAchievement).to.be.called; | ||||
|       expect(achievement.displayAchievement).to.be.calledWith('ultimateGear', {size: 'md'}); | ||||
|     }); | ||||
|  | ||||
|     it('does not open ultimate gear set achievement modal if no new ultimate-gear-achievement notification is received', function() { | ||||
|       rootScope.$digest(); | ||||
|       rootScope.$digest(); | ||||
|  | ||||
|       expect(achievement.displayAchievement).to.not.be.calledWith('ultimateGear', {size: 'md'}); | ||||
|     }); | ||||
|   }); | ||||
|  | ||||
|   describe('User rebirth achievement notification watch', function() { | ||||
|     it('opens rebirth achievement modal when a rebirth-achievement notification is received', function() { | ||||
|       rootScope.$digest(); | ||||
|       rootScope.userNotifications.push({type: 'REBIRTH_ACHIEVEMENT'}); | ||||
|       rootScope.$digest(); | ||||
|  | ||||
|       expect(achievement.displayAchievement).to.be.called; | ||||
|       expect(achievement.displayAchievement).to.be.calledWith('rebirth'); | ||||
|     }); | ||||
|  | ||||
|     it('does not open rebirth achievement modal if no new rebirth-achievement notification is received', function() { | ||||
|       rootScope.$digest(); | ||||
|       rootScope.$digest(); | ||||
|  | ||||
|       expect(achievement.displayAchievement).to.not.be.calledWith('rebirth'); | ||||
|     }); | ||||
|   }); | ||||
|  | ||||
|   describe('User contributor achievement notification watch', function() { | ||||
|     it('opens contributor achievement modal when a new-contributor-level notification is received', function() { | ||||
|       rootScope.$digest(); | ||||
|       rootScope.userNotifications.push({type: 'NEW_CONTRIBUTOR_LEVEL'}); | ||||
|       rootScope.$digest(); | ||||
|  | ||||
|       expect(achievement.displayAchievement).to.be.called; | ||||
|       expect(achievement.displayAchievement).to.be.calledWith('contributor', {size: 'md'}); | ||||
|     }); | ||||
|  | ||||
|     it('does not open contributor achievement modal if no new new-contributor-level notification is received', function() { | ||||
|       rootScope.$digest(); | ||||
|       rootScope.$digest(); | ||||
|  | ||||
|       expect(achievement.displayAchievement).to.not.be.calledWith('contributor', {size: 'md'}); | ||||
|     }); | ||||
|   }); | ||||
| }); | ||||
| @@ -1,489 +0,0 @@ | ||||
| 'use strict'; | ||||
|  | ||||
| describe("Party Controller", function() { | ||||
|   var scope, ctrl, user, User, questsService, groups, achievement, rootScope, $controller, deferred, party; | ||||
|  | ||||
|   beforeEach(function() { | ||||
|     user = specHelper.newUser(), | ||||
|     user._id = "unique-user-id"; | ||||
|     User = { | ||||
|       user: user, | ||||
|       sync: sandbox.spy(), | ||||
|       set: sandbox.spy() | ||||
|     }; | ||||
|  | ||||
|     party = specHelper.newGroup({ | ||||
|       _id: "unique-party-id", | ||||
|       type: 'party', | ||||
|       members: ['leader-id'] // Ensure we wouldn't pass automatically. | ||||
|     }); | ||||
|  | ||||
|     module(function($provide) { | ||||
|       $provide.value('User', User); | ||||
|     }); | ||||
|  | ||||
|     inject(function(_$rootScope_, _$controller_, Groups, Quests, _$q_, Achievement){ | ||||
|  | ||||
|       rootScope = _$rootScope_; | ||||
|  | ||||
|       scope = _$rootScope_.$new(); | ||||
|  | ||||
|       $controller = _$controller_; | ||||
|  | ||||
|       groups = Groups; | ||||
|       questsService = Quests; | ||||
|       achievement = Achievement; | ||||
|  | ||||
|       // Load RootCtrl to ensure shared behaviors are loaded | ||||
|       $controller('RootCtrl',  {$scope: scope, User: User}); | ||||
|  | ||||
|       ctrl = $controller('PartyCtrl', {$scope: scope, User: User}); | ||||
|     }); | ||||
|   }); | ||||
|  | ||||
|   describe('initialization', function() { | ||||
|     var groupResponse; | ||||
|  | ||||
|     function initializeControllerWithStubbedState() { | ||||
|       inject(function(_$state_) { | ||||
|         var state = _$state_; | ||||
|         sandbox.stub(state, 'is').returns(true); | ||||
|  | ||||
|         var syncParty = sinon.stub(groups.Group, 'syncParty') | ||||
|         syncParty.returns(Promise.resolve(groupResponse)); | ||||
|  | ||||
|         var froceSyncParty = sinon.stub(groups, 'party') | ||||
|         froceSyncParty.returns(Promise.resolve(groupResponse)); | ||||
|  | ||||
|         $controller('PartyCtrl', { $scope: scope, $state: state, User: User }); | ||||
|         expect(state.is).to.be.calledOnce; | ||||
|       }); | ||||
|     }; | ||||
|  | ||||
|     beforeEach(function() { | ||||
|       sandbox.stub(achievement, 'displayAchievement'); | ||||
|     }); | ||||
|  | ||||
|     context('party has 1 member', function() { | ||||
|       it('awards no new achievements', function() { | ||||
|         groupResponse = {_id: "test", type: "party", memberCount: 1}; | ||||
|  | ||||
|         initializeControllerWithStubbedState(); | ||||
|  | ||||
|         expect(User.set).to.not.be.called; | ||||
|         expect(achievement.displayAchievement).to.not.be.called; | ||||
|       }); | ||||
|     }); | ||||
|  | ||||
|     context('party has 2 members', function() { | ||||
|       context('user does not have "Party Up" achievement', function() { | ||||
|         it('awards "Party Up" achievement', function(done) { | ||||
|           groupResponse = {_id: "test", type: "party", memberCount: 2}; | ||||
|  | ||||
|           initializeControllerWithStubbedState(); | ||||
|  | ||||
|           setTimeout(function() { | ||||
|             expect(User.set).to.be.calledOnce; | ||||
|             expect(User.set).to.be.calledWith( | ||||
|               { 'achievements.partyUp': true } | ||||
|             ); | ||||
|             expect(achievement.displayAchievement).to.be.calledOnce; | ||||
|             expect(achievement.displayAchievement).to.be.calledWith('partyUp'); | ||||
|             done(); | ||||
|           }, 1000); | ||||
|         }); | ||||
|       }); | ||||
|     }); | ||||
|  | ||||
|     context('party has 4 members', function() { | ||||
|  | ||||
|       beforeEach(function() { | ||||
|         groupResponse = {_id: "test", type: "party", memberCount: 4}; | ||||
|       }); | ||||
|  | ||||
|       context('user has "Party Up" but not "Party On" achievement', function() { | ||||
|         it('awards "Party On" achievement', function(done) { | ||||
|           user.achievements.partyUp = true; | ||||
|  | ||||
|           initializeControllerWithStubbedState(); | ||||
|  | ||||
|           setTimeout(function(){ | ||||
|             expect(User.set).to.be.calledOnce; | ||||
|             expect(User.set).to.be.calledWith( | ||||
|               { 'achievements.partyOn': true } | ||||
|             ); | ||||
|             expect(achievement.displayAchievement).to.be.calledOnce; | ||||
|             expect(achievement.displayAchievement).to.be.calledWith('partyOn'); | ||||
|             done(); | ||||
|           }, 1000); | ||||
|         }); | ||||
|       }); | ||||
|  | ||||
|       context('user has neither "Party Up" nor "Party On" achievements', function() { | ||||
|         it('awards "Party Up" and "Party On" achievements', function(done) { | ||||
|           initializeControllerWithStubbedState(); | ||||
|  | ||||
|           setTimeout(function(){ | ||||
|             expect(User.set).to.have.been.called; | ||||
|             expect(User.set).to.be.calledWith( | ||||
|               { 'achievements.partyUp': true} | ||||
|             ); | ||||
|             expect(User.set).to.be.calledWith( | ||||
|               { 'achievements.partyOn': true} | ||||
|             ); | ||||
|             expect(achievement.displayAchievement).to.have.been.called; | ||||
|             expect(achievement.displayAchievement).to.be.calledWith('partyUp'); | ||||
|             expect(achievement.displayAchievement).to.be.calledWith('partyOn'); | ||||
|             done(); | ||||
|           }, 1000); | ||||
|         }); | ||||
|       }); | ||||
|  | ||||
|       context('user has both "Party Up" and "Party On" achievements', function() { | ||||
|         it('awards no new achievements', function() { | ||||
|           user.achievements.partyUp = true; | ||||
|           user.achievements.partyOn = true; | ||||
|  | ||||
|           initializeControllerWithStubbedState(); | ||||
|  | ||||
|           expect(User.set).to.not.be.called; | ||||
|           expect(achievement.displayAchievement).to.not.be.called; | ||||
|         }); | ||||
|       }); | ||||
|     }); | ||||
|   }); | ||||
|  | ||||
|   describe("create", function() { | ||||
|     var partyStub; | ||||
|  | ||||
|     beforeEach(function () { | ||||
|       partyStub = sinon.stub(groups.Group, "create"); | ||||
|       partyStub.returns(Promise.resolve(party)); | ||||
|       sinon.stub(rootScope, 'hardRedirect'); | ||||
|     }); | ||||
|  | ||||
|     it("creates a new party", function() { | ||||
|       var group = { | ||||
|         type: 'party', | ||||
|       }; | ||||
|       scope.create(group); | ||||
|       expect(partyStub).to.be.calledOnce; | ||||
|       //@TODO: Check user party  console.log(User.user.party.id) | ||||
|     }); | ||||
|   }); | ||||
|  | ||||
|   describe('questAccept', function() { | ||||
|     var sendAction; | ||||
|     var memberResponse; | ||||
|  | ||||
|     beforeEach(function() { | ||||
|       scope.group = { | ||||
|         quest: { members: { 'user-id': true } } | ||||
|       }; | ||||
|  | ||||
|       memberResponse = {members: {another: true}}; | ||||
|       sinon.stub(questsService, 'sendAction') | ||||
|       questsService.sendAction.returns(Promise.resolve(memberResponse)); | ||||
|     }); | ||||
|  | ||||
|     it('calls Quests.sendAction', function() { | ||||
|       scope.questAccept(); | ||||
|  | ||||
|       expect(questsService.sendAction).to.be.calledOnce; | ||||
|       expect(questsService.sendAction).to.be.calledWith('quests/accept'); | ||||
|     }); | ||||
|  | ||||
|  | ||||
|     it('updates quest object with new participants list', function(done) { | ||||
|       scope.group.quest = { | ||||
|         members: { user: true, another: true } | ||||
|       }; | ||||
|  | ||||
|       setTimeout(function(){ | ||||
|         expect(scope.group.quest).to.eql(memberResponse); | ||||
|         done(); | ||||
|       }, 1000); | ||||
|  | ||||
|       scope.questAccept(); | ||||
|     }); | ||||
|   }); | ||||
|  | ||||
|   describe('questReject', function() { | ||||
|     var memberResponse; | ||||
|  | ||||
|     beforeEach(function() { | ||||
|       scope.group = { | ||||
|         quest: { members: { 'user-id': true } } | ||||
|       }; | ||||
|  | ||||
|       memberResponse = {members: {another: true}}; | ||||
|       var sendAction = sinon.stub(questsService, 'sendAction') | ||||
|       sendAction.returns(Promise.resolve(memberResponse)); | ||||
|     }); | ||||
|  | ||||
|     it('calls Quests.sendAction', function() { | ||||
|       scope.questReject(); | ||||
|  | ||||
|       expect(questsService.sendAction).to.be.calledOnce; | ||||
|       expect(questsService.sendAction).to.be.calledWith('quests/reject'); | ||||
|     }); | ||||
|  | ||||
|  | ||||
|     it('updates quest object with new participants list', function(done) { | ||||
|       scope.group.quest = { | ||||
|         members: { user: true, another: true } | ||||
|       }; | ||||
|  | ||||
|       setTimeout(function(){ | ||||
|         expect(scope.group.quest).to.eql(memberResponse); | ||||
|         done(); | ||||
|       }, 1000); | ||||
|  | ||||
|       scope.questReject(); | ||||
|     }); | ||||
|   }); | ||||
|  | ||||
|   describe('questCancel', function() { | ||||
|     var party, cancelSpy, windowSpy, memberResponse; | ||||
|  | ||||
|     beforeEach(function() { | ||||
|       scope.group = { | ||||
|         quest: { members: { 'user-id': true } } | ||||
|       }; | ||||
|  | ||||
|       memberResponse = {members: {another: true}}; | ||||
|       sinon.stub(questsService, 'sendAction') | ||||
|       questsService.sendAction.returns(Promise.resolve(memberResponse)); | ||||
|     }); | ||||
|  | ||||
|     it('calls Quests.sendAction when alert box is confirmed', function() { | ||||
|       sandbox.stub(window, "confirm").returns(true); | ||||
|  | ||||
|       scope.questCancel(); | ||||
|  | ||||
|       expect(window.confirm).to.be.calledOnce; | ||||
|       expect(window.confirm).to.be.calledWith(window.env.t('sureCancel')); | ||||
|       expect(questsService.sendAction).to.be.calledOnce; | ||||
|       expect(questsService.sendAction).to.be.calledWith('quests/cancel'); | ||||
|     }); | ||||
|  | ||||
|     it('does not call Quests.sendAction when alert box is not confirmed', function() { | ||||
|       sandbox.stub(window, "confirm").returns(false); | ||||
|  | ||||
|       scope.questCancel(); | ||||
|  | ||||
|       expect(window.confirm).to.be.calledOnce; | ||||
|       expect(questsService.sendAction).to.not.be.called; | ||||
|     }); | ||||
|   }); | ||||
|  | ||||
|   describe('questAbort', function() { | ||||
|     var memberResponse; | ||||
|  | ||||
|     beforeEach(function() { | ||||
|       scope.group = { | ||||
|         quest: { members: { 'user-id': true } } | ||||
|       }; | ||||
|  | ||||
|       memberResponse = {members: {another: true}}; | ||||
|       sinon.stub(questsService, 'sendAction') | ||||
|       questsService.sendAction.returns(Promise.resolve(memberResponse)); | ||||
|     }); | ||||
|  | ||||
|     it('calls Quests.sendAction when two alert boxes are confirmed', function() { | ||||
|       sandbox.stub(window, "confirm", function(){return true}); | ||||
|  | ||||
|       scope.questAbort(); | ||||
|       expect(window.confirm).to.be.calledTwice; | ||||
|       expect(window.confirm).to.be.calledWith(window.env.t('sureAbort')); | ||||
|       expect(window.confirm).to.be.calledWith(window.env.t('doubleSureAbort')); | ||||
|  | ||||
|       expect(questsService.sendAction).to.be.calledOnce; | ||||
|       expect(questsService.sendAction).to.be.calledWith('quests/abort'); | ||||
|     }); | ||||
|  | ||||
|     it('does not call Quests.sendAction when first alert box is not confirmed', function() { | ||||
|       sandbox.stub(window, "confirm", function(){return false}); | ||||
|  | ||||
|       scope.questAbort(); | ||||
|  | ||||
|       expect(window.confirm).to.be.calledOnce; | ||||
|       expect(window.confirm).to.be.calledWith(window.env.t('sureAbort')); | ||||
|       expect(window.confirm).to.not.be.calledWith(window.env.t('doubleSureAbort')); | ||||
|  | ||||
|       expect(questsService.sendAction).to.not.be.called; | ||||
|     }); | ||||
|  | ||||
|     it('does not call Quests.sendAction when first alert box is confirmed but second one is not', function() { | ||||
|       // Hack to confirm first window, but not second | ||||
|       // Should not be necessary when we upgrade sinon | ||||
|       var shouldReturn = false; | ||||
|       sandbox.stub(window, 'confirm', function(){ | ||||
|         shouldReturn = !shouldReturn; | ||||
|         return shouldReturn; | ||||
|       }); | ||||
|  | ||||
|       scope.questAbort(); | ||||
|  | ||||
|       expect(window.confirm).to.be.calledTwice; | ||||
|       expect(window.confirm).to.be.calledWith(window.env.t('sureAbort')); | ||||
|       expect(window.confirm).to.be.calledWith(window.env.t('doubleSureAbort')); | ||||
|       expect(questsService.sendAction).to.not.be.called; | ||||
|     }); | ||||
|   }); | ||||
|  | ||||
|   describe('#questLeave', function() { | ||||
|     var memberResponse; | ||||
|  | ||||
|     beforeEach(function() { | ||||
|       scope.group = { | ||||
|         quest: { members: { 'user-id': true } } | ||||
|       }; | ||||
|  | ||||
|       memberResponse = {members: {another: true}}; | ||||
|       sinon.stub(questsService, 'sendAction') | ||||
|       questsService.sendAction.returns(Promise.resolve(memberResponse)); | ||||
|     }); | ||||
|  | ||||
|     it('calls Quests.sendAction when alert box is confirmed', function() { | ||||
|       sandbox.stub(window, "confirm").returns(true); | ||||
|  | ||||
|       scope.questLeave(); | ||||
|  | ||||
|       expect(window.confirm).to.be.calledOnce; | ||||
|       expect(window.confirm).to.be.calledWith(window.env.t('sureLeave')); | ||||
|       expect(questsService.sendAction).to.be.calledOnce; | ||||
|       expect(questsService.sendAction).to.be.calledWith('quests/leave'); | ||||
|     }); | ||||
|  | ||||
|     it('does not call Quests.sendAction when alert box is not confirmed', function() { | ||||
|       sandbox.stub(window, "confirm").returns(false); | ||||
|  | ||||
|       scope.questLeave(); | ||||
|  | ||||
|       expect(window.confirm).to.be.calledOnce; | ||||
|       questsService.sendAction.should.not.have.been.calledOnce; | ||||
|     }); | ||||
|  | ||||
|     it('updates quest object with new participants list', function(done) { | ||||
|       scope.group.quest = { | ||||
|         members: { user: true, another: true } | ||||
|       }; | ||||
|       sandbox.stub(window, "confirm").returns(true); | ||||
|  | ||||
|       setTimeout(function(){ | ||||
|         expect(scope.group.quest).to.eql(memberResponse); | ||||
|         done(); | ||||
|       }, 1000); | ||||
|  | ||||
|       scope.questLeave(); | ||||
|     }); | ||||
|   }); | ||||
|  | ||||
|   describe('clickStartQuest', function() { | ||||
|     beforeEach(function() { | ||||
|       sandbox.stub(rootScope, 'openModal'); | ||||
|       sandbox.stub(rootScope.$state, 'go'); | ||||
|     }); | ||||
|  | ||||
|     it('opens quest modal if user has a quest', function() { | ||||
|       user.items.quests = { | ||||
|         whale: 1 | ||||
|       }; | ||||
|  | ||||
|       scope.clickStartQuest(); | ||||
|  | ||||
|       expect(rootScope.$state.go).to.not.be.called; | ||||
|       expect(rootScope.openModal).to.be.calledOnce; | ||||
|       expect(rootScope.openModal).to.be.calledWith( | ||||
|         'ownedQuests', | ||||
|         { controller: 'InventoryCtrl' } | ||||
|       ); | ||||
|     }); | ||||
|  | ||||
|     it('does not open modal if user has no quests', function() { | ||||
|       user.items.quests = { }; | ||||
|  | ||||
|       scope.clickStartQuest(); | ||||
|  | ||||
|       expect(rootScope.openModal).to.not.be.called; | ||||
|       expect(rootScope.$state.go).to.be.calledOnce; | ||||
|       expect(rootScope.$state.go).to.be.calledWith('options.inventory.quests'); | ||||
|     }); | ||||
|  | ||||
|     it('does not open modal if user had quests previously, but does not now', function() { | ||||
|       user.items.quests = { | ||||
|         whale: 0, | ||||
|         atom1: 0 | ||||
|       }; | ||||
|  | ||||
|       scope.clickStartQuest(); | ||||
|  | ||||
|       expect(rootScope.openModal).to.not.be.called; | ||||
|       expect(rootScope.$state.go).to.be.calledOnce; | ||||
|       expect(rootScope.$state.go).to.be.calledWith('options.inventory.quests'); | ||||
|     }); | ||||
|   }); | ||||
|  | ||||
|   describe('#leaveOldPartyAndJoinNewParty', function() { | ||||
|     beforeEach(function() { | ||||
|       sandbox.stub(scope, 'join'); | ||||
|       groups.data.party = { _id: 'old-party' }; | ||||
|       var groupLeave = sandbox.stub(groups.Group, 'leave'); | ||||
|       groupLeave.returns(Promise.resolve({})); | ||||
|       sandbox.stub(groups, 'party').returns({ | ||||
|         _id: 'old-party' | ||||
|       }); | ||||
|       sandbox.stub(window, 'confirm').returns(true); | ||||
|     }); | ||||
|  | ||||
|     it('does nothing if user declines confirmation', function() { | ||||
|       window.confirm.returns(false); | ||||
|       scope.leaveOldPartyAndJoinNewParty('some-id', 'some-name'); | ||||
|  | ||||
|       expect(groups.Group.leave).to.not.be.called; | ||||
|     }) | ||||
|  | ||||
|     it('leaves user\'s current party', function() { | ||||
|       scope.leaveOldPartyAndJoinNewParty('some-id', 'some-name'); | ||||
|  | ||||
|       expect(groups.Group.leave).to.be.calledOnce; | ||||
|       expect(groups.Group.leave).to.be.calledWith('old-party', false); | ||||
|     }); | ||||
|  | ||||
|     it('joins the new party', function(done) { | ||||
|       scope.leaveOldPartyAndJoinNewParty('some-id', 'some-name'); | ||||
|  | ||||
|       setTimeout(function() { | ||||
|         expect(scope.join).to.be.calledOnce; | ||||
|         expect(scope.join).to.be.calledWith({id: 'some-id', name: 'some-name'}); | ||||
|         done(); | ||||
|       }, 1000); | ||||
|     }); | ||||
|   }); | ||||
|  | ||||
|   describe('#canEditQuest', function() { | ||||
|     var party; | ||||
|  | ||||
|     beforeEach(function() { | ||||
|       party = specHelper.newGroup({ | ||||
|         type: 'party', | ||||
|         leader: {}, | ||||
|         quest: {} | ||||
|       }); | ||||
|       scope.group = party; | ||||
|     }); | ||||
|  | ||||
|     it('returns false if user is not the quest leader', function() { | ||||
|       party.quest.leader = 'another-user'; | ||||
|  | ||||
|       expect(scope.canEditQuest(party)).to.eql(false); | ||||
|     }); | ||||
|  | ||||
|     it('returns true if user is quest leader', function() { | ||||
|       party.quest.leader = 'unique-user-id'; | ||||
|  | ||||
|       expect(scope.canEditQuest(party)).to.eql(true); | ||||
|     }); | ||||
|   }); | ||||
| }); | ||||
| @@ -1,187 +0,0 @@ | ||||
| 'use strict'; | ||||
|  | ||||
| describe('Root Controller', function() { | ||||
|   var scope, rootscope, user, User, notification, ctrl, $httpBackend; | ||||
|  | ||||
|   beforeEach(function () { | ||||
|     module(function($provide) { | ||||
|       $provide.value('User', {}); | ||||
|       $provide.service('$templateCache', function () { | ||||
|         return { | ||||
|           get: function () {}, | ||||
|           put: function () {} | ||||
|         } | ||||
|       }); | ||||
|     }); | ||||
|  | ||||
|     inject(function($rootScope, $controller, _$httpBackend_, Notification) { | ||||
|       scope = $rootScope.$new(); | ||||
|       scope.loginUsername = 'user'; | ||||
|       scope.loginPassword = 'pass'; | ||||
|  | ||||
|       rootscope = $rootScope; | ||||
|  | ||||
|       $httpBackend = _$httpBackend_; | ||||
|  | ||||
|       notification = Notification; | ||||
|       sandbox.stub(notification, 'text'); | ||||
|       sandbox.stub(notification, 'markdown'); | ||||
|  | ||||
|       user = specHelper.newUser(); | ||||
|       User = {user: user}; | ||||
|       User.save = sandbox.spy(); | ||||
|       User.sync = sandbox.spy(); | ||||
|  | ||||
|       $httpBackend.whenGET(/partials/).respond(); | ||||
|  | ||||
|       ctrl = $controller('RootCtrl', {$scope: scope, User: User}); | ||||
|     }); | ||||
|   }); | ||||
|  | ||||
|   describe('contribText', function(){ | ||||
|     it('shows contributor level text', function(){ | ||||
|       expect(scope.contribText()).to.eql(undefined); | ||||
|       expect(scope.contribText(null, {npc: 'NPC'})).to.eql('NPC'); | ||||
|       expect(scope.contribText({level: 0, text: 'Blacksmith'})).to.eql(undefined); | ||||
|       expect(scope.contribText({level: 1, text: 'Blacksmith'})).to.eql('Friend Blacksmith'); | ||||
|       expect(scope.contribText({level: 2, text: 'Blacksmith'})).to.eql('Friend Blacksmith'); | ||||
|       expect(scope.contribText({level: 3, text: 'Blacksmith'})).to.eql('Elite Blacksmith'); | ||||
|       expect(scope.contribText({level: 4, text: 'Blacksmith'})).to.eql('Elite Blacksmith'); | ||||
|       expect(scope.contribText({level: 5, text: 'Blacksmith'})).to.eql('Champion Blacksmith'); | ||||
|       expect(scope.contribText({level: 6, text: 'Blacksmith'})).to.eql('Champion Blacksmith'); | ||||
|       expect(scope.contribText({level: 7, text: 'Blacksmith'})).to.eql('Legendary Blacksmith'); | ||||
|       expect(scope.contribText({level: 8, text: 'Blacksmith'})).to.eql('Guardian Blacksmith'); | ||||
|       expect(scope.contribText({level: 9, text: 'Blacksmith'})).to.eql('Heroic Blacksmith'); | ||||
|       expect(scope.contribText({level: 9, text: 'Blacksmith'}, {npc: 'NPC'})).to.eql('NPC'); | ||||
|     }); | ||||
|   }); | ||||
|  | ||||
|   describe('castEnd', function(){ | ||||
|     var task_target, type; | ||||
|  | ||||
|     beforeEach(function(){ | ||||
|       task_target = { | ||||
|         id: 'task-id', | ||||
|         text: 'task' | ||||
|       }; | ||||
|       type = 'task'; | ||||
|       scope.spell = { | ||||
|         target: 'task', | ||||
|         key: 'fireball', | ||||
|         mana: 10, | ||||
|         text: function() { return env.t('spellWizardFireballText') }, | ||||
|         cast: function(){} | ||||
|       }; | ||||
|       rootscope.applyingAction = true; | ||||
|     }); | ||||
|  | ||||
|     context('fails', function(){ | ||||
|       it('exits early if there is no applying action', function(){ | ||||
|         rootscope.applyingAction = null; | ||||
|         expect(scope.castEnd(task_target, type)).to.be.eql('No applying action'); | ||||
|       }); | ||||
|  | ||||
|       it('sends notification if target is invalid', function(){ | ||||
|         scope.spell.target = 'not_the_same_target'; | ||||
|  | ||||
|         scope.castEnd(task_target, type); | ||||
|  | ||||
|         notification.text.should.have.been.calledWith(window.env.t('invalidTarget')); | ||||
|       }); | ||||
|     }); | ||||
|  | ||||
|     context('succeeds', function(){ | ||||
|       it('sets scope.spell and rootScope.applyingAction to falsy values', function(){ | ||||
|  | ||||
|         scope.castEnd(task_target, type); | ||||
|  | ||||
|         expect(rootscope.applyingAction).to.eql(false); | ||||
|         expect(scope.spell).to.eql(null); | ||||
|       }); | ||||
|  | ||||
|       it('calls $scope.spell.cast', function(){ | ||||
|         // Kind of a hack, would prefer to use sinon.spy, | ||||
|         // but scope.spell gets turned to null in scope.castEnd | ||||
|         var spellWasCast = false; | ||||
|         scope.spell.cast = function(){ spellWasCast = true }; | ||||
|  | ||||
|         scope.castEnd(task_target, type); | ||||
|  | ||||
|         expect(spellWasCast).to.eql(true); | ||||
|       }); | ||||
|  | ||||
|       it('calls cast endpoint', function() { | ||||
|         $httpBackend.expectPOST(/cast/).respond(201); | ||||
|         scope.castEnd(task_target, type); | ||||
|  | ||||
|         $httpBackend.flush(); | ||||
|       }); | ||||
|  | ||||
|       it('sends notification that spell was cast on task', function() { | ||||
|         $httpBackend.expectPOST(/cast/).respond(201); | ||||
|         scope.castEnd(task_target, type); | ||||
|         $httpBackend.flush(); | ||||
|  | ||||
|         expect(notification.markdown).to.be.calledOnce; | ||||
|         expect(notification.markdown).to.be.calledWith('You cast Burst of Flames on task.'); | ||||
|         expect(User.sync).to.be.calledOnce; | ||||
|       }); | ||||
|  | ||||
|       it('sends notification that spell was cast on user', function() { | ||||
|         var user_target = { | ||||
|           profile: { name: 'Lefnire' } | ||||
|         }; | ||||
|         scope.spell = { | ||||
|           target: 'user', | ||||
|           key: 'snowball', | ||||
|           mana: 0, | ||||
|           text: function() { return env.t('spellSpecialSnowballAuraText') }, | ||||
|           cast: function(){} | ||||
|         }; | ||||
|         $httpBackend.expectPOST(/cast/).respond(201); | ||||
|         scope.castEnd(user_target, 'user'); | ||||
|         $httpBackend.flush(); | ||||
|  | ||||
|         expect(notification.markdown).to.be.calledOnce; | ||||
|         expect(notification.markdown).to.be.calledWith('You cast Snowball on Lefnire.'); | ||||
|         expect(User.sync).to.be.calledOnce; | ||||
|       }); | ||||
|  | ||||
|       it('sends notification that spell was cast on party', function() { | ||||
|         var party_target = {}; | ||||
|         scope.spell = { | ||||
|           target: 'party', | ||||
|           key: 'healAll', | ||||
|           mana: 25, | ||||
|           text: function() { return env.t('spellHealerHealAllText') }, | ||||
|           cast: function(){} | ||||
|         }; | ||||
|         $httpBackend.expectPOST(/cast/).respond(201); | ||||
|         scope.castEnd(party_target, 'party'); | ||||
|         $httpBackend.flush(); | ||||
|  | ||||
|         expect(notification.markdown).to.be.calledOnce; | ||||
|         expect(notification.markdown).to.be.calledWith('You cast Blessing for the party.'); | ||||
|         expect(User.sync).to.be.calledOnce; | ||||
|       }); | ||||
|  | ||||
|       it('sends notification that spell was cast on self', function() { | ||||
|         var self_target = {}; | ||||
|         scope.spell = { | ||||
|           target: 'self', | ||||
|           key: 'stealth', | ||||
|           mana: 45, | ||||
|           text: function() { return env.t('spellRogueStealthText') }, | ||||
|           cast: function(){} | ||||
|         }; | ||||
|         $httpBackend.expectPOST(/cast/).respond(201); | ||||
|         scope.castEnd(self_target, 'self'); | ||||
|         $httpBackend.flush(); | ||||
|  | ||||
|         expect(notification.markdown).to.be.calledOnce; | ||||
|         expect(notification.markdown).to.be.calledWith('You cast Stealth.'); | ||||
|         expect(User.sync).to.be.calledOnce; | ||||
|       }); | ||||
|     }); | ||||
|   }); | ||||
| }); | ||||
| @@ -1,367 +0,0 @@ | ||||
| 'use strict'; | ||||
|  | ||||
| describe('Settings Controller', function () { | ||||
|   var rootScope, scope, $httpBackend, user, User, ctrl, Notification; | ||||
|  | ||||
|   const actionClickEvent = { | ||||
|     target: document.createElement('button'), | ||||
|   }; | ||||
|  | ||||
|   beforeEach(function () { | ||||
|     module(function($provide) { | ||||
|       user = specHelper.newUser(); | ||||
|       User = { | ||||
|         set: sandbox.stub(), | ||||
|         reroll: sandbox.stub(), | ||||
|         rebirth: sandbox.stub(), | ||||
|         releasePets: sandbox.stub(), | ||||
|         releaseMounts: sandbox.stub(), | ||||
|         releaseBoth: sandbox.stub(), | ||||
|         setCustomDayStart: sandbox.stub(), | ||||
|         user: user | ||||
|       }; | ||||
|  | ||||
|       User.user.ops = { | ||||
|         reroll: sandbox.stub(), | ||||
|         rebirth: sandbox.stub(), | ||||
|         releasePets: sandbox.stub(), | ||||
|         releaseMounts: sandbox.stub(), | ||||
|         releaseBoth: sandbox.stub(), | ||||
|       }; | ||||
|  | ||||
|       Notification = { | ||||
|         error: sandbox.stub(), | ||||
|         text: sandbox.stub() | ||||
|       }; | ||||
|  | ||||
|       $provide.value('Notification', Notification); | ||||
|       $provide.value('User', User); | ||||
|       $provide.value('Guide', sandbox.stub()); | ||||
|     }); | ||||
|  | ||||
|     inject(function(_$rootScope_, _$controller_, _$httpBackend_) { | ||||
|       scope = _$rootScope_.$new(); | ||||
|       rootScope = _$rootScope_; | ||||
|       $httpBackend = _$httpBackend_; | ||||
|  | ||||
|       $httpBackend.whenGET(/partials/).respond(); | ||||
|  | ||||
|       // Load RootCtrl to ensure shared behaviors are loaded | ||||
|       _$controller_('RootCtrl',  {$scope: scope, User: User, Notification: Notification}); | ||||
|  | ||||
|       ctrl = _$controller_('SettingsCtrl', {$scope: scope, User: User, Notification: Notification}); | ||||
|     }); | ||||
|   }); | ||||
|  | ||||
|   describe('#openDayStartModal', function () { | ||||
|     beforeEach(function () { | ||||
|       sandbox.stub(rootScope, 'openModal'); | ||||
|       sandbox.stub(window, 'alert'); | ||||
|     }); | ||||
|  | ||||
|     it('opens the day start modal', function () { | ||||
|       scope.openDayStartModal(5); | ||||
|  | ||||
|       expect(rootScope.openModal).to.be.calledOnce; | ||||
|       expect(rootScope.openModal).to.be.calledWith('change-day-start', {scope: scope}); | ||||
|     }); | ||||
|  | ||||
|     it('sets nextCron variable', function () { | ||||
|       expect(scope.nextCron).to.not.exist; | ||||
|  | ||||
|       scope.openDayStartModal(5); | ||||
|  | ||||
|       expect(scope.nextCron).to.exist; | ||||
|     }); | ||||
|  | ||||
|     it('calculates the next time cron will run', function () { | ||||
|       var fakeCurrentTime = new Date(2013, 3, 1, 3, 12).getTime(); | ||||
|       var expectedTime = new Date(2013, 3, 1, 5, 0, 0).getTime(); | ||||
|       sandbox.useFakeTimers(fakeCurrentTime); | ||||
|  | ||||
|       scope.openDayStartModal(5); | ||||
|  | ||||
|       expect(scope.nextCron).to.eq(expectedTime); | ||||
|     }); | ||||
|  | ||||
|     it('calculates the next time cron will run and adds a day if cron would have already passed', function () { | ||||
|       var fakeCurrentTime = new Date(2013, 3, 1, 8, 12).getTime(); | ||||
|       var expectedTime = new Date(2013, 3, 2, 5, 0, 0).getTime(); | ||||
|       sandbox.useFakeTimers(fakeCurrentTime); | ||||
|  | ||||
|       scope.openDayStartModal(5); | ||||
|  | ||||
|       expect(scope.nextCron).to.eq(expectedTime); | ||||
|     }); | ||||
|   }); | ||||
|  | ||||
|   describe('#saveDayStart', function () { | ||||
|     it('updates user\'s custom day start', function () { | ||||
|       scope.dayStart = 5; | ||||
|       scope.saveDayStart(); | ||||
|  | ||||
|       expect(User.setCustomDayStart).to.be.calledWith(5); | ||||
|     }); | ||||
|   }); | ||||
|  | ||||
|   context('Player Reroll', function () { | ||||
|     describe('#reroll', function () { | ||||
|       beforeEach(function () { | ||||
|         scope.clickReroll(actionClickEvent); | ||||
|       }); | ||||
|  | ||||
|       it('destroys the previous popover if it exists', function () { | ||||
|         sandbox.spy($.fn, 'popover'); | ||||
|  | ||||
|         scope.reroll(false); | ||||
|  | ||||
|         expect(scope.popoverEl).to.exist; | ||||
|         expect($.fn.popover).to.be.calledWith('destroy'); | ||||
|       }); | ||||
|  | ||||
|       it('doesn\'t call reroll when not confirmed', function () { | ||||
|         scope.reroll(false); | ||||
|  | ||||
|         expect(user.ops.reroll).to.not.be.calledOnce; | ||||
|       }); | ||||
|  | ||||
|       it('calls reroll on the user when confirmed', function () { | ||||
|         sandbox.stub(rootScope.$state, 'go'); | ||||
|  | ||||
|         scope.reroll(true); | ||||
|  | ||||
|         expect(User.reroll).to.be.calledWith({}); | ||||
|       }); | ||||
|  | ||||
|       it('navigates to the tasks page when confirmed', function () { | ||||
|         sandbox.stub(rootScope.$state, 'go'); | ||||
|  | ||||
|         scope.reroll(true); | ||||
|  | ||||
|         expect(rootScope.$state.go).to.be.calledWith('tasks'); | ||||
|       }); | ||||
|     }); | ||||
|  | ||||
|     describe('#clickReroll', function () { | ||||
|       it('displays a confirmation popover for the user', function () { | ||||
|         sandbox.spy($.fn, 'popover'); | ||||
|  | ||||
|         scope.clickReroll(actionClickEvent); | ||||
|  | ||||
|         expect($.fn.popover).to.be.calledWith('destroy'); | ||||
|         expect($.fn.popover).to.be.calledWith('show'); | ||||
|       }); | ||||
|     }); | ||||
|   }); | ||||
|  | ||||
|   context('Player Rebirth', function () { | ||||
|     describe('#rebirth', function () { | ||||
|       beforeEach(function () { | ||||
|         scope.clickRebirth(actionClickEvent); | ||||
|       }); | ||||
|  | ||||
|       it('destroys the previous popover if it exists', function () { | ||||
|         sandbox.spy($.fn, 'popover'); | ||||
|  | ||||
|         scope.rebirth(false); | ||||
|  | ||||
|         expect(scope.popoverEl).to.exist; | ||||
|         expect($.fn.popover).to.be.calledWith('destroy'); | ||||
|       }); | ||||
|  | ||||
|       it('doesn\'t call rebirth when not confirmed', function () { | ||||
|         scope.rebirth(false); | ||||
|  | ||||
|         expect(user.ops.rebirth).to.not.be.calledOnce; | ||||
|       }); | ||||
|  | ||||
|       it('calls rebirth on the user when confirmed', function () { | ||||
|         sandbox.stub(rootScope.$state, 'go'); | ||||
|  | ||||
|         scope.rebirth(true); | ||||
|  | ||||
|         expect(User.rebirth).to.be.calledWith({}); | ||||
|       }); | ||||
|  | ||||
|       it('navigates to tasks page when confirmed', function () { | ||||
|         sandbox.stub(rootScope.$state, 'go'); | ||||
|  | ||||
|         scope.rebirth(true); | ||||
|  | ||||
|         expect(rootScope.$state.go).to.be.calledWith('tasks'); | ||||
|       }); | ||||
|     }); | ||||
|  | ||||
|     describe('#clickRebirth', function () { | ||||
|       it('displays a confirmation popover for the user', function () { | ||||
|         sandbox.spy($.fn, 'popover'); | ||||
|  | ||||
|         scope.clickRebirth(actionClickEvent); | ||||
|  | ||||
|         expect($.fn.popover).to.be.calledWith('destroy'); | ||||
|         expect($.fn.popover).to.be.calledWith('show'); | ||||
|       }); | ||||
|     }); | ||||
|   }) | ||||
|  | ||||
|   context('Releasing pets and mounts', function () { | ||||
|     describe('#release', function () { | ||||
|       beforeEach(function () { | ||||
|         scope.clickRelease('dummy', actionClickEvent); | ||||
|  | ||||
|         sandbox.stub(rootScope.$state, 'go'); | ||||
|       }); | ||||
|  | ||||
|       it('destroys the previous popover if it exists', function () { | ||||
|         sandbox.spy($.fn, 'popover'); | ||||
|  | ||||
|         scope.releaseAnimals('', false); | ||||
|  | ||||
|         expect($.fn.popover).to.be.calledWith('destroy'); | ||||
|       }); | ||||
|  | ||||
|       it('doesn\'t call any release method if type is not provided', function () { | ||||
|         scope.releaseAnimals(); | ||||
|  | ||||
|         expect(User.releasePets).to.not.be.called; | ||||
|         expect(User.releaseMounts).to.not.be.called; | ||||
|         expect(User.releaseBoth).to.not.be.called; | ||||
|       }); | ||||
|  | ||||
|       it('doesn\'t redirect to tasks page if type is not provided', function () { | ||||
|         scope.releaseAnimals(); | ||||
|  | ||||
|         expect(rootScope.$state.go).to.not.be.called; | ||||
|       }) | ||||
|  | ||||
|       it('calls releasePets when "pets" is provided', function () { | ||||
|         scope.releaseAnimals('pets'); | ||||
|  | ||||
|         expect(User.releasePets).to.be.calledOnce; | ||||
|       }); | ||||
|  | ||||
|       it('navigates to the tasks page when "pets" is provided', function () { | ||||
|         scope.releaseAnimals('pets'); | ||||
|  | ||||
|         expect(rootScope.$state.go).to.be.calledOnce; | ||||
|       }); | ||||
|  | ||||
|       it('calls releaseMounts when "mounts" is provided', function () { | ||||
|         scope.releaseAnimals('mounts'); | ||||
|  | ||||
|         expect(User.releaseMounts).to.be.calledOnce; | ||||
|       }); | ||||
|  | ||||
|       it('navigates to the tasks page when "mounts" is provided', function () { | ||||
|         scope.releaseAnimals('mounts'); | ||||
|  | ||||
|         expect(rootScope.$state.go).to.be.calledOnce; | ||||
|       }); | ||||
|  | ||||
|       it('calls releaseBoth when "both" is provided', function () { | ||||
|         scope.releaseAnimals('both'); | ||||
|  | ||||
|         expect(User.releaseBoth).to.be.calledOnce; | ||||
|       }); | ||||
|  | ||||
|       it('navigates to the tasks page when "both" is provided', function () { | ||||
|         scope.releaseAnimals('both'); | ||||
|  | ||||
|         expect(rootScope.$state.go).to.be.calledOnce; | ||||
|       }); | ||||
|  | ||||
|       it('does not call release functions when non-applicable argument is passed in', function () { | ||||
|         scope.releaseAnimals('dummy'); | ||||
|  | ||||
|         expect(User.releasePets).to.not.be.called; | ||||
|         expect(User.releaseMounts).to.not.be.called; | ||||
|         expect(User.releaseBoth).to.not.be.called; | ||||
|       }); | ||||
|     }); | ||||
|  | ||||
|     describe('#clickRelease', function () { | ||||
|       it('displays a confirmation popover for the user', function () { | ||||
|         sandbox.spy($.fn, 'popover'); | ||||
|         scope.clickRelease('dummy', actionClickEvent); | ||||
|  | ||||
|         expect($.fn.popover).to.be.calledWith('destroy'); | ||||
|         expect($.fn.popover).to.be.called; | ||||
|         expect($.fn.popover).to.be.calledWith('show'); | ||||
|       }); | ||||
|     }); | ||||
|   }); | ||||
|  | ||||
|   context('Validating coupons', function () { | ||||
|     describe('#applyCoupon', function () { | ||||
|       it('displays an error when an invalid coupon is applied', function () { | ||||
|         $httpBackend | ||||
|           .whenPOST('/api/v3/coupons/validate/INVALID_COUPON?userV=undefined') | ||||
|           .respond(200, { | ||||
|             success: true, | ||||
|             data: { | ||||
|               valid: false | ||||
|             }, | ||||
|             notifications: [], | ||||
|             userV: 'undefined' | ||||
|           }); | ||||
|  | ||||
|         scope.applyCoupon('INVALID_COUPON'); | ||||
|  | ||||
|         $httpBackend.flush(); | ||||
|  | ||||
|         expect(Notification.error).to.be.called; | ||||
|         expect(Notification.error).to.be.calledWith(env.t('invalidCoupon'), true); | ||||
|       }); | ||||
|  | ||||
|       it('displays an confirmation when a valid coupon is applied', function () { | ||||
|         $httpBackend | ||||
|           .whenPOST('/api/v3/coupons/validate/VALID_COUPON?userV=undefined') | ||||
|           .respond(200, { | ||||
|             success: true, | ||||
|             data: { | ||||
|               valid: true | ||||
|             }, | ||||
|             notifications: [], | ||||
|             userV: 'undefined' | ||||
|           }); | ||||
|  | ||||
|         scope.applyCoupon('VALID_COUPON'); | ||||
|  | ||||
|         $httpBackend.flush(); | ||||
|  | ||||
|         expect(Notification.error).to.not.be.called; | ||||
|         expect(Notification.text).to.be.calledWith('Coupon applied!'); | ||||
|       }); | ||||
|     }); | ||||
|   }); | ||||
|  | ||||
|   context('Fixing character values', function () { | ||||
|     describe('#restore', function () { | ||||
|       var blankRestoreValues = { | ||||
|         stats: { | ||||
|           hp: 0, | ||||
|           exp: 0, | ||||
|           gp: 0, | ||||
|           lvl: 0, | ||||
|           mp: 0, | ||||
|         }, | ||||
|         achievements: { | ||||
|           streak: 0, | ||||
|         }, | ||||
|       }; | ||||
|  | ||||
|       it('doesn\'t update character values when level is less than 1', function () { | ||||
|         scope.restoreValues = blankRestoreValues; | ||||
|         scope.restore(); | ||||
|         expect(User.set).to.not.be.called; | ||||
|       }); | ||||
|  | ||||
|       it('updates character values when level is at least 1', function () { | ||||
|         scope.restoreValues = blankRestoreValues; | ||||
|         scope.restoreValues.stats.lvl = 1; | ||||
|         scope.restore(); | ||||
|         expect(User.set).to.be.called; | ||||
|       }); | ||||
|     }); | ||||
|   }); | ||||
| }); | ||||
| @@ -1,41 +0,0 @@ | ||||
| describe('Sortable Inventory Controller', () => { | ||||
|   let scope; | ||||
|  | ||||
|   beforeEach(inject(($rootScope, $controller) => { | ||||
|     scope = $rootScope.$new(); | ||||
|     $controller('SortableInventoryController', {$scope: scope}); | ||||
|   })); | ||||
|  | ||||
|   it('defaults scope.order to set', () => { | ||||
|     expect(scope.order).to.eql('set') | ||||
|   }); | ||||
|  | ||||
|   describe('#setOrder', () => { | ||||
|     it('sets sort criteria for all standard attributes', () =>{ | ||||
|       let oldOrder = scope.order; | ||||
|  | ||||
|       let attrs = [ | ||||
|         'constitution', | ||||
|         'intelligence', | ||||
|         'perception', | ||||
|         'strength', | ||||
|         'set' | ||||
|       ]; | ||||
|  | ||||
|       attrs.forEach((attribute) => { | ||||
|         scope.setOrder(attribute); | ||||
|         expect(scope.order).to.exist; | ||||
|         expect(scope.order).to.not.eql(oldOrder); | ||||
|         oldOrder = scope.order; | ||||
|       }); | ||||
|     }); | ||||
|  | ||||
|     it('does nothing when missing sort criteria', () =>{ | ||||
|       scope.order = null; | ||||
|  | ||||
|       scope.setOrder('foooo'); | ||||
|  | ||||
|       expect(scope.order).to.not.exist; | ||||
|     }); | ||||
|   }); | ||||
| }); | ||||
| @@ -1,76 +0,0 @@ | ||||
| 'use strict'; | ||||
|  | ||||
| describe('Tasks Controller', function() { | ||||
|   var $rootScope, shared, scope, user, User, ctrl; | ||||
|  | ||||
|   beforeEach(function() { | ||||
|     user = specHelper.newUser(); | ||||
|     User = { | ||||
|       user: user | ||||
|     }; | ||||
|  | ||||
|     User.deleteTask = sandbox.stub(); | ||||
|     User.user.ops = { | ||||
|       deleteTask:  sandbox.stub(), | ||||
|     }; | ||||
|     module(function($provide) { | ||||
|       $provide.value('User', User); | ||||
|       $provide.value('Guide', {}); | ||||
|     }); | ||||
|  | ||||
|     inject(function($rootScope, $controller, Shared){ | ||||
|  | ||||
|       scope = $rootScope.$new(); | ||||
|       shared = Shared; | ||||
|       $controller('RootCtrl',  {$scope: scope, User: User}); | ||||
|  | ||||
|       ctrl = $controller('TasksCtrl', {$scope: scope, User: User}); | ||||
|  | ||||
|     }); | ||||
|   }); | ||||
|  | ||||
|   describe('editTask', function() { | ||||
|     it('is Tasks.editTask', function() { | ||||
|       inject(function(Tasks) { | ||||
|         // @TODO: Currently we override the task function in the challenge ctrl, but we should abstract it again | ||||
|         // expect(scope.editTask).to.eql(Tasks.editTask); | ||||
|       }); | ||||
|     }); | ||||
|   }); | ||||
|  | ||||
|   describe('removeTask', function() { | ||||
|     var task; | ||||
|  | ||||
|     beforeEach(function() { | ||||
|       sandbox.stub(window, 'confirm'); | ||||
|       task = specHelper.newTodo(); | ||||
|     }); | ||||
|  | ||||
|     it('asks user to confirm deletion', function() { | ||||
|       scope.removeTask(task); | ||||
|       expect(window.confirm).to.be.calledOnce; | ||||
|     }); | ||||
|  | ||||
|     it('does not remove task if not confirmed', function() { | ||||
|       window.confirm.returns(false); | ||||
|       scope.removeTask(task); | ||||
|       expect(User.deleteTask).to.not.be.called; | ||||
|     }); | ||||
|  | ||||
|     it('removes task', function() { | ||||
|       window.confirm.returns(true); | ||||
|       scope.removeTask(task); | ||||
|       expect(User.deleteTask).to.be.calledOnce; | ||||
|     }); | ||||
|   }); | ||||
|  | ||||
|   describe('watch to updateStore', function() { | ||||
|     it('updates itemStore when user gear changes', function() { | ||||
|       sinon.stub(shared, 'updateStore').returns({item: true}); | ||||
|       user.items.gear.owned.foo = true; | ||||
|  | ||||
|       scope.$digest(); | ||||
|       expect(scope.itemStore).to.eql({item: true}); | ||||
|     }); | ||||
|   }); | ||||
| }); | ||||
| @@ -1,69 +0,0 @@ | ||||
| 'use strict'; | ||||
|  | ||||
| describe('User Controller', function() { | ||||
|   var $rootScope, $window, User, shared, scope, ctrl, content; | ||||
|  | ||||
|   beforeEach(function() { | ||||
|     module(function ($provide) { | ||||
|       var user = specHelper.newUser(); | ||||
|       User = {user: user} | ||||
|       $provide.value('Guide', sandbox.stub()); | ||||
|       $provide.value('User', User); | ||||
|       $provide.value('Achievement', sandbox.stub()); | ||||
|       $provide.value('Social', sandbox.stub()); | ||||
|       $provide.value('Shared', { | ||||
|         achievements: { | ||||
|           getAchievementsForProfile: sandbox.stub() | ||||
|         }, | ||||
|         shops: { | ||||
|           getBackgroundShopSets: sandbox.stub() | ||||
|         } | ||||
|       }); | ||||
|       $provide.value('Content', { | ||||
|         loginIncentives: sandbox.stub() | ||||
|       }) | ||||
|     }); | ||||
|  | ||||
|     inject(function($rootScope, $controller, User, Content) { | ||||
|       scope = $rootScope.$new(); | ||||
|       content = Content; | ||||
|       $controller('RootCtrl', { $scope: scope, User: User}); | ||||
|       ctrl = $controller('UserCtrl', { $scope: scope, User: User, $window: $window}); | ||||
|     }); | ||||
|   }); | ||||
|  | ||||
|   describe('getProgressDisplay', function() { | ||||
|  | ||||
|     beforeEach(() => { | ||||
|       sandbox.stub(window.env, 't'); | ||||
|       window.env.t.onFirstCall().returns('Progress until next'); | ||||
|     }); | ||||
|  | ||||
|     it('should return initial progress', function() { | ||||
|       scope.profile.loginIncentives = 0; | ||||
|       content.loginIncentives = [{ | ||||
|         nextRewardAt: 1, | ||||
|         reward: true | ||||
|       }]; | ||||
|       var actual = scope.getProgressDisplay(); | ||||
|       expect(actual.trim()).to.eql('Progress until next 0/1'); | ||||
|     }); | ||||
|  | ||||
|     it('should return progress between next reward and current reward', function() { | ||||
|       scope.profile.loginIncentives = 1; | ||||
|       content.loginIncentives = [{ | ||||
|         nextRewardAt: 1, | ||||
|         reward: true | ||||
|       }, { | ||||
|         prevRewardAt: 0, | ||||
|         nextRewardAt: 2, | ||||
|         reward: true | ||||
|       }, { | ||||
|         prevRewardAt: 1, | ||||
|         nextRewardAt: 3 | ||||
|       }]; | ||||
|       var actual = scope.getProgressDisplay(); | ||||
|       expect(actual.trim()).to.eql('Progress until next 0/1'); | ||||
|     }); | ||||
|   }); | ||||
| }); | ||||
| @@ -1,30 +0,0 @@ | ||||
| 'use strict'; | ||||
|  | ||||
| describe('focusElement Directive', function() { | ||||
|   var elementToFocus, scope; | ||||
|  | ||||
|   beforeEach(module('habitrpg')); | ||||
|  | ||||
|   beforeEach(inject(function($rootScope, $compile) { | ||||
|     scope = $rootScope.$new(); | ||||
|  | ||||
|     scope.focusThisLink = false; | ||||
|     var element = '<input data-focus-element="focusThisLink" />'; | ||||
|  | ||||
|     elementToFocus = $compile(element)(scope); | ||||
|     scope.$digest(); | ||||
|   })); | ||||
|  | ||||
|   it('places focus on the element it is applied to when the expression it binds to evaluates to true', inject(function($timeout) { | ||||
|     var focusSpy = sandbox.spy(); | ||||
|  | ||||
|     elementToFocus.appendTo(document.body); | ||||
|     elementToFocus.on('focus', focusSpy); | ||||
|     scope.focusThisLink = true; | ||||
|     scope.$digest(); | ||||
|  | ||||
|     $timeout.flush(); | ||||
|     expect(document.activeElement.dataset.focusElement).to.eql("focusThisLink"); | ||||
|     expect(focusSpy).to.have.been.called; | ||||
|     })); | ||||
| }); | ||||
| @@ -1,89 +0,0 @@ | ||||
| 'use strict'; | ||||
|  | ||||
| describe('fromNow Directive', function() { | ||||
|   var element, scope; | ||||
|   var fromNow = 'recently'; | ||||
|   var diff    = 0; | ||||
|  | ||||
|   beforeEach(module('habitrpg')); | ||||
|  | ||||
|   beforeEach(inject(function($rootScope, $compile) { | ||||
|     scope = $rootScope.$new(); | ||||
|     scope.message = {}; | ||||
|  | ||||
|     sandbox.stub(window, 'moment').returns({ | ||||
|       fromNow: function() { return fromNow }, | ||||
|       diff:    function() { return diff    } | ||||
|     }); | ||||
|  | ||||
|     element = "<p from-now></p>"; | ||||
|  | ||||
|     element = $compile(element)(scope); | ||||
|     scope.$digest(); | ||||
|   })); | ||||
|  | ||||
|   afterEach(function() { | ||||
|     window.moment.restore(); | ||||
|   }); | ||||
|  | ||||
|   it('sets the element text to the elapsed time', function() { | ||||
|     expect(element.text()).to.eql('recently'); | ||||
|   }); | ||||
|  | ||||
|   describe('when the elapsed time is less than an hour', function() { | ||||
|     beforeEach(inject(function($compile) { | ||||
|       fromNow = 'recently'; | ||||
|       diff    = 0; | ||||
|  | ||||
|       element = $compile('<p from-now></p>')(scope); | ||||
|       scope.$digest(); | ||||
|     })); | ||||
|  | ||||
|     it('updates the elapsed time every minute', inject(function($interval) { | ||||
|       fromNow = 'later'; | ||||
|  | ||||
|       expect(element.text()).to.eql('recently'); | ||||
|       $interval.flush(60001); | ||||
|  | ||||
|       expect(element.text()).to.eql('later'); | ||||
|     })); | ||||
|  | ||||
|     it('moves to hourly updates after an hour', inject(function($timeout, $interval) { | ||||
|       diff = 61; | ||||
|  | ||||
|       $timeout.flush(); | ||||
|       $interval.flush(60001); | ||||
|  | ||||
|       fromNow = 'later'; | ||||
|  | ||||
|       $interval.flush(60001); | ||||
|       expect(element.text()).to.eql('recently'); | ||||
|  | ||||
|       $interval.flush(3600000); | ||||
|       expect(element.text()).to.eql('later'); | ||||
|     })); | ||||
|   }); | ||||
|  | ||||
|   describe('when the elapsed time is more than an hour', function() { | ||||
|     beforeEach(inject(function($compile) { | ||||
|       fromNow = 'recently'; | ||||
|       diff    = 65; | ||||
|  | ||||
|       element = $compile('<p from-now></p>')(scope); | ||||
|       scope.$digest(); | ||||
|     })); | ||||
|  | ||||
|     it('updates the elapsed time every hour', inject(function($interval) { | ||||
|       fromNow = 'later'; | ||||
|  | ||||
|       expect(element.text()).to.eql('recently'); | ||||
|  | ||||
|       $interval.flush(60001); | ||||
|       expect(element.text()).to.eql('recently'); | ||||
|  | ||||
|       $interval.flush(3600000); | ||||
|       expect(element.text()).to.eql('later'); | ||||
|     })); | ||||
|   }); | ||||
|  | ||||
| }); | ||||
| @@ -1,35 +0,0 @@ | ||||
| describe('task Directive', () => { | ||||
|   var compile, scope, directiveElem, $modal; | ||||
|  | ||||
|   beforeEach(function(){ | ||||
|     module(function($provide) { | ||||
|       $modal = { | ||||
|         open: sandbox.spy(), | ||||
|       }; | ||||
|       $provide.value('$modal', $modal); | ||||
|     }); | ||||
|  | ||||
|     inject(function($compile, $rootScope, $templateCache) { | ||||
|       compile = $compile; | ||||
|       scope = $rootScope.$new(); | ||||
|  | ||||
|       $templateCache.put('templates/task.html', '<div>Task</div>'); | ||||
|     }); | ||||
|  | ||||
|  | ||||
|     directiveElem = getCompiledElement(); | ||||
|   }); | ||||
|  | ||||
|   function getCompiledElement(){ | ||||
|     var element = angular.element('<task></task>'); | ||||
|     var compiledElement = compile(element)(scope); | ||||
|     scope.$digest(); | ||||
|     return compiledElement; | ||||
|   } | ||||
|  | ||||
|   xit('opens task note modal', () => { | ||||
|     scope.showNoteDetails(); | ||||
|  | ||||
|     expect($modal.open).to.be.calledOnce; | ||||
|   }); | ||||
| }) | ||||
| @@ -1,33 +0,0 @@ | ||||
| describe('roundLargeNumbers', function() { | ||||
|  | ||||
|   beforeEach(module('habitrpg')); | ||||
|  | ||||
|   it('returns same number if less than 1000', inject(function(roundLargeNumbersFilter) { | ||||
|     for(var num = 0; num < 1000; num++) { | ||||
|       expect(roundLargeNumbersFilter(num)).to.eql(num); | ||||
|     }; | ||||
|   })); | ||||
|  | ||||
|   it('truncates number and appends "k" if number is 1000-999999', inject(function(roundLargeNumbersFilter) { | ||||
|     expect(roundLargeNumbersFilter(999.01)).to.eql("1.0k"); | ||||
|     expect(roundLargeNumbersFilter(1000)).to.eql("1.0k"); | ||||
|     expect(roundLargeNumbersFilter(3284.12)).to.eql("3.3k"); | ||||
|     expect(roundLargeNumbersFilter(52983.99)).to.eql("53.0k"); | ||||
|     expect(roundLargeNumbersFilter(452983.99)).to.eql("453.0k"); | ||||
|     expect(roundLargeNumbersFilter(999999)).to.eql("1000.0k"); | ||||
|   })); | ||||
|  | ||||
|   it('truncates number and appends "m" if number is 1000000-999999999', inject(function(roundLargeNumbersFilter) { | ||||
|     expect(roundLargeNumbersFilter(999999.01)).to.eql("1.0m"); | ||||
|     expect(roundLargeNumbersFilter(1000000)).to.eql("1.0m"); | ||||
|     expect(roundLargeNumbersFilter(3284124.12)).to.eql("3.3m"); | ||||
|     expect(roundLargeNumbersFilter(52983105.99)).to.eql("53.0m"); | ||||
|     expect(roundLargeNumbersFilter(452983410.99)).to.eql("453.0m"); | ||||
|     expect(roundLargeNumbersFilter(999999999)).to.eql("1000.0m"); | ||||
|   })); | ||||
|  | ||||
|   it('truncates number and appends b" if number is greater than 999999999', inject(function(roundLargeNumbersFilter) { | ||||
|     expect(roundLargeNumbersFilter(999999999.01)).to.eql("1.0b"); | ||||
|     expect(roundLargeNumbersFilter(1423985738.54)).to.eql("1.4b"); | ||||
|   })); | ||||
| }); | ||||
| @@ -1,35 +0,0 @@ | ||||
| describe('filter', function() { | ||||
|  | ||||
|   beforeEach(module('habitrpg')); | ||||
|  | ||||
|   describe('gold', function() { | ||||
|     it('rounds down decimal values', inject(function(goldFilter) { | ||||
|       expect(goldFilter(10)).to.eql(10); | ||||
|       expect(goldFilter(10.0)).to.eql(10); | ||||
|       expect(goldFilter(10.1)).to.eql(10); | ||||
|       expect(goldFilter(10.2)).to.eql(10); | ||||
|       expect(goldFilter(10.3)).to.eql(10); | ||||
|       expect(goldFilter(10.4)).to.eql(10); | ||||
|       expect(goldFilter(10.5)).to.eql(10); | ||||
|       expect(goldFilter(10.6)).to.eql(10); | ||||
|       expect(goldFilter(10.7)).to.eql(10); | ||||
|       expect(goldFilter(10.8)).to.eql(10); | ||||
|       expect(goldFilter(10.9)).to.eql(10); | ||||
|       expect(goldFilter(11)).to.eql(11); | ||||
|     })); | ||||
|   }); | ||||
|  | ||||
|   describe('silver', function() { | ||||
|     it('converts decimal value of gold to silver', inject(function(silverFilter) { | ||||
|       expect(silverFilter(10)).to.be.closeTo(0, 1); | ||||
|       expect(silverFilter(10.01)).to.be.closeTo(1, 1); | ||||
|       expect(silverFilter(10.05)).to.be.closeTo(5, 1); | ||||
|       expect(silverFilter(10.17)).to.be.closeTo(17, 1); | ||||
|       expect(silverFilter(10.23)).to.be.closeTo(23, 1); | ||||
|       expect(silverFilter(10.25)).to.be.closeTo(25, 1); | ||||
|       expect(silverFilter(10.53)).to.be.closeTo(53, 1); | ||||
|       expect(silverFilter(10.75)).to.be.closeTo(75, 1); | ||||
|       expect(silverFilter(10.99)).to.be.closeTo(99, 1); | ||||
|     })); | ||||
|   }); | ||||
| }); | ||||
| @@ -1,94 +0,0 @@ | ||||
| 'use strict'; | ||||
|  | ||||
| describe('Task Ordering Filters', function() { | ||||
|   var filter, orderBySpy; | ||||
|  | ||||
|   beforeEach(function() { | ||||
|     orderBySpy = sandbox.spy(); | ||||
|  | ||||
|     module(function($provide) { | ||||
|       $provide.value('orderByFilter', orderBySpy); | ||||
|     }); | ||||
|     inject(function($rootScope, $filter) { | ||||
|       filter  = $filter; | ||||
|     }); | ||||
|   }); | ||||
|  | ||||
|   describe('conditionalOrderBy', function() { | ||||
|     describe('when the predicate is true', function() { | ||||
|       it('delegates the arguments to the orderBy filter', function() { | ||||
|         filter('conditionalOrderBy')('array', true, 'sortPredicate', 'reverseOrder'); | ||||
|         expect(orderBySpy).to.have.been.calledWith('array','sortPredicate','reverseOrder'); | ||||
|       }); | ||||
|     }); | ||||
|  | ||||
|     describe('when the predicate is false', function() { | ||||
|       it('returns the initial array', function() { | ||||
|         expect(filter('conditionalOrderBy')([1,2,3], false)).to.eql([1,2,3]); | ||||
|       }); | ||||
|     }); | ||||
|   }); | ||||
|  | ||||
|   describe('filterByTaskInfo', function () { | ||||
|     it('returns undefined when no input given', function () { | ||||
|       expect(filter('filterByTaskInfo')()).to.eql(undefined); | ||||
|     }); | ||||
|  | ||||
|     it('returns all tasks if term is not a string', function () { | ||||
|       var tasks  = [1, 2, 3]; | ||||
|       expect(filter('filterByTaskInfo')(tasks, undefined)).to.eql(tasks); | ||||
|       expect(filter('filterByTaskInfo')(tasks, [])).to.eql(tasks); | ||||
|       expect(filter('filterByTaskInfo')(tasks, new Date())).to.eql(tasks); | ||||
|     }); | ||||
|  | ||||
|     it('returns tasks if term is an empty string', function () { | ||||
|       var tasks  = [1, 2, 3]; | ||||
|       expect(filter('filterByTaskInfo')(tasks, '')).to.eql(tasks); | ||||
|     }); | ||||
|  | ||||
|     it('filters items by text', function () { | ||||
|       var tasks = [ | ||||
|         { text: 'foo' }, | ||||
|         { text: 'some text that contains foo' }, | ||||
|         { text: 'some text that should not be matched' } | ||||
|       ]; | ||||
|  | ||||
|       expect(filter('filterByTaskInfo')(tasks, 'foo')).to.eql([tasks[0], tasks[1]]); | ||||
|     }); | ||||
|  | ||||
|     it('filters items by notes', function () { | ||||
|       var tasks = [ | ||||
|         { text: 'some text', notes: 'foo' }, | ||||
|         { text: 'some text', notes: 'a note that contains foo' }, | ||||
|         { text: 'some text', notes: 'some text' }, | ||||
|         { text: 'some text' } | ||||
|       ]; | ||||
|  | ||||
|       expect(filter('filterByTaskInfo')(tasks, 'foo')).to.eql([tasks[0], tasks[1]]); | ||||
|     }); | ||||
|  | ||||
|     it('filters items by checklists', function () { | ||||
|       var tasks = [ | ||||
|         { text: 'foo' }, | ||||
|         { text: 'foo', notes: 'bar', checklist: [ {text: "checkListToFind"} ] }, | ||||
|         { text: 'foo', notes: 'bar', checklist: [ {text: "checkListToNotFind"} ] } | ||||
|       ]; | ||||
|  | ||||
|       expect(filter('filterByTaskInfo')(tasks, 'checkListToFind')).to.eql([tasks[1]]); | ||||
|     }); | ||||
|  | ||||
|     it('only includes task once, even with multiple matches in checklist', function() { | ||||
|       var tasks = [ | ||||
|         { | ||||
|           text: 'foo', notes: 'bar', checklist: [ | ||||
|             {text: "checkListToFind"}, | ||||
|             {text: "checkListToFind"}, | ||||
|             {text: "checkListToFind"} | ||||
|           ] | ||||
|         } | ||||
|       ]; | ||||
|  | ||||
|       expect(filter('filterByTaskInfo')(tasks, 'checkListToFind')).to.eql([tasks[0]]); | ||||
|     }); | ||||
|   }); | ||||
| }); | ||||
| @@ -1,20 +0,0 @@ | ||||
| describe('timezoneOffsetToUtc', function() { | ||||
|  | ||||
|   beforeEach(module('habitrpg')); | ||||
|  | ||||
|   it('formats the timezone offset with a - sign if the offset is positive', inject(function(timezoneOffsetToUtcFilter) { | ||||
|     expect(timezoneOffsetToUtcFilter(90)).to.eql('UTC-1:30'); | ||||
|   })); | ||||
|  | ||||
|   it('formats the timezone offset with a + sign if the offset is negative', inject(function(timezoneOffsetToUtcFilter) { | ||||
|     expect(timezoneOffsetToUtcFilter(-525)).to.eql('UTC+8:45'); | ||||
|   })); | ||||
|  | ||||
|   it('prepends the minutes with a 0 if the minute the offset is less than 10', inject(function(timezoneOffsetToUtcFilter) { | ||||
|     expect(timezoneOffsetToUtcFilter(60)).to.eql('UTC-1:00'); | ||||
|   })); | ||||
|  | ||||
|   it('returns the string UTC+0:00 if the offset is 0', inject(function(timezoneOffsetToUtcFilter) { | ||||
|     expect(timezoneOffsetToUtcFilter(0)).to.eql('UTC+0:00'); | ||||
|   })); | ||||
| }); | ||||
| @@ -1,98 +0,0 @@ | ||||
| // Karma configuration | ||||
| // http://karma-runner.github.io/0.10/config/configuration-file.html | ||||
|  | ||||
| module.exports = function karmaConfig (config) { | ||||
|   config.set({ | ||||
|     // base path, that will be used to resolve files and exclude | ||||
|     basePath: '', | ||||
|  | ||||
|     // testing framework to use (jasmine/mocha/qunit/...) | ||||
|     frameworks: ['mocha', 'chai', 'chai-as-promised', 'sinon-chai'], | ||||
|  | ||||
|     // list of files / patterns to load in the browser | ||||
|     files: [ | ||||
|       '../../../website/client-old/bower_components/jquery/dist/jquery.js', | ||||
|       '../../../website/client-old/bower_components/pnotify/jquery.pnotify.js', | ||||
|       '../../../website/client-old/bower_components/angular/angular.js', | ||||
|       '../../../website/client-old/bower_components/angular-loading-bar/build/loading-bar.min.js', | ||||
|       '../../../website/client-old/bower_components/angular-resource/angular-resource.min.js', | ||||
|       '../../../website/client-old/bower_components/hello/dist/hello.all.min.js', | ||||
|       '../../../website/client-old/bower_components/angular-sanitize/angular-sanitize.js', | ||||
|       '../../../website/client-old/bower_components/bootstrap/dist/js/bootstrap.js', | ||||
|       '../../../website/client-old/bower_components/angular-bootstrap/ui-bootstrap.js', | ||||
|       '../../../website/client-old/bower_components/angular-bootstrap/ui-bootstrap-tpls.js', | ||||
|       '../../../website/client-old/bower_components/angular-ui-router/release/angular-ui-router.js', | ||||
|       '../../../website/client-old/bower_components/angular-filter/dist/angular-filter.js', | ||||
|       '../../../website/client-old/bower_components/angular-ui/build/angular-ui.js', | ||||
|       '../../../website/client-old/bower_components/angular-ui-utils/ui-utils.min.js', | ||||
|       '../../../website/client-old/bower_components/Angular-At-Directive/src/at.js', | ||||
|       '../../../website/client-old/bower_components/Angular-At-Directive/src/caret.js', | ||||
|       '../../../website/client-old/bower_components/angular-mocks/angular-mocks.js', | ||||
|       '../../../website/client-old/bower_components/ngInfiniteScroll/build/ng-infinite-scroll.js', | ||||
|       '../../../website/client-old/bower_components/select2/select2.js', | ||||
|       '../../../website/client-old/bower_components/angular-ui-select2/src/select2.js', | ||||
|       '../../../website/client-old/bower_components/habitica-markdown/dist/habitica-markdown.min.js', | ||||
|       '../../../website/client-old/js/habitrpg-shared.js', | ||||
|  | ||||
|       '../../../test/client-old/spec/mocks/**/*.js', | ||||
|  | ||||
|       '../../../website/client-old/js/env.js', | ||||
|       '../../../website/client-old/js/app.js', | ||||
|       '../../../website/client-old/js/config.js', | ||||
|  | ||||
|       '../../../website/client-old/js/services/**/*.js', | ||||
|       '../../../website/client-old/js/filters/**/*.js', | ||||
|       '../../../website/client-old/js/directives/**/*.js', | ||||
|       '../../../website/client-old/js/controllers/**/*.js', | ||||
|       '../../../website/client-old/js/components/**/*.js', | ||||
|  | ||||
|       '../../../test/client-old/spec/specHelper.js', | ||||
|       '../../../test/client-old/spec/**/*.js', | ||||
|     ], | ||||
|  | ||||
|     // list of files / patterns to exclude | ||||
|     exclude: [], | ||||
|  | ||||
|     // web server port | ||||
|     port: 8080, | ||||
|  | ||||
|     // level of logging | ||||
|     // possible values: LOG_DISABLE || LOG_ERROR || LOG_WARN || LOG_INFO || LOG_DEBUG | ||||
|     logLevel: config.LOG_INFO, | ||||
|  | ||||
|  | ||||
|     // enable / disable watching file and executing tests whenever any file changes | ||||
|     autoWatch: true, | ||||
|  | ||||
|  | ||||
|     // Start these browsers, currently available: | ||||
|     // - Chrome | ||||
|     // - ChromeCanary | ||||
|     // - Firefox | ||||
|     // - Opera | ||||
|     // - Safari (only Mac) | ||||
|     // - PhantomJS | ||||
|     // - IE (only Windows) | ||||
|     browsers: ['PhantomJS'], | ||||
|  | ||||
|     preprocessors: { | ||||
|       '../../../website/client-old/js/**/*.js': ['coverage'], | ||||
|       '../../../test/**/*.js': ['babel'], | ||||
|     }, | ||||
|  | ||||
|     coverageReporter: { | ||||
|       reporters: [ | ||||
|         { type: 'lcov', subdir: '.' }, | ||||
|         { type: 'text-summary' }, | ||||
|       ], | ||||
|       dir: '../../../coverage/karma', | ||||
|     }, | ||||
|  | ||||
|     // Enable mocha-style reporting, for better test visibility | ||||
|     reporters: ['mocha', 'coverage'], | ||||
|  | ||||
|     // Continuous Integration mode | ||||
|     // if true, it capture browsers, run tests and exit | ||||
|     singleRun: false, | ||||
|   }); | ||||
| }; | ||||
| @@ -1,5 +0,0 @@ | ||||
| var sandbox = sinon.sandbox.create(); | ||||
|  | ||||
| afterEach(function() { | ||||
|   sandbox.restore(); | ||||
| }); | ||||
| @@ -1,8 +0,0 @@ | ||||
| 'use strict' | ||||
|  | ||||
| var analyticsMock = { | ||||
|   login: sandbox.spy(), | ||||
|   register: sandbox.spy(), | ||||
|   updateUser: sandbox.spy(), | ||||
|   track: sandbox.spy() | ||||
| }; | ||||
| @@ -1,55 +0,0 @@ | ||||
| 'use strict'; | ||||
|  | ||||
| describe('achievementServices', function() { | ||||
|   var achievementService, rootScope; | ||||
|  | ||||
|   beforeEach(function() { | ||||
|     rootScope = { 'openModal': sandbox.stub() }; | ||||
|     module(function($provide) { | ||||
|       $provide.value('$rootScope', rootScope); | ||||
|     }); | ||||
|  | ||||
|     inject(function(Achievement) { | ||||
|       achievementService = Achievement; | ||||
|     }); | ||||
|   }); | ||||
|  | ||||
|   describe('#displayAchievement', function() { | ||||
|     it('passes given achievement name to openModal', function() { | ||||
|       achievementService.displayAchievement('beastMaster'); | ||||
|  | ||||
|       expect(rootScope.openModal).to.be.calledOnce; | ||||
|       expect(rootScope.openModal).to.be.calledWith('achievements/beastMaster'); | ||||
|     }); | ||||
|  | ||||
|     it('calls openModal with UserCtrl and small modal size if no other size is given', function() { | ||||
|       achievementService.displayAchievement('test'); | ||||
|  | ||||
|       expect(rootScope.openModal).to.be.calledOnce; | ||||
|       expect(rootScope.openModal).to.be.calledWith( | ||||
|         'achievements/test', | ||||
|         { controller: 'UserCtrl', size: 'sm' } | ||||
|       ); | ||||
|     }); | ||||
|  | ||||
|     it('calls openModal with UserCtrl and specified modal size if one is given', function() { | ||||
|       achievementService.displayAchievement('test', {size: 'md'}); | ||||
|  | ||||
|       expect(rootScope.openModal).to.be.calledOnce; | ||||
|       expect(rootScope.openModal).to.be.calledWith( | ||||
|         'achievements/test', | ||||
|         { controller: 'UserCtrl', size: 'md' } | ||||
|       ); | ||||
|     }); | ||||
|  | ||||
|     it('calls openModal with UserCtrl and default \'sm\' size if invalid size is given', function() { | ||||
|       achievementService.displayAchievement('test', {size: 'INVALID_SIZE'}); | ||||
|  | ||||
|       expect(rootScope.openModal).to.be.calledOnce; | ||||
|       expect(rootScope.openModal).to.be.calledWith( | ||||
|         'achievements/test', | ||||
|         { controller: 'UserCtrl', size: 'sm' } | ||||
|       ); | ||||
|     }); | ||||
|   }); | ||||
| }); | ||||
| @@ -1,282 +0,0 @@ | ||||
| 'use strict'; | ||||
|  | ||||
| describe('Analytics Service', function () { | ||||
|   var analytics, user, clock; | ||||
|  | ||||
|   beforeEach(function() { | ||||
|     clock = sandbox.useFakeTimers(); | ||||
|     sandbox.stub(window, 'refresher', function(){return true}); | ||||
|     user = specHelper.newUser({ | ||||
|       contributor: {}, | ||||
|       purchased: { plan: {} }, | ||||
|     }); | ||||
|     user.flags.tour = { intro: null }; | ||||
|  | ||||
|     module(function($provide) { | ||||
|       $provide.value('User', {user: user}); | ||||
|     }); | ||||
|  | ||||
|     inject(function(Analytics) { | ||||
|       analytics = Analytics; | ||||
|     }); | ||||
|   }); | ||||
|  | ||||
|   context('functions', function() { | ||||
|  | ||||
|     describe('register', function() { | ||||
|  | ||||
|       beforeEach(function() { | ||||
|         sandbox.stub(amplitude, 'setUserId'); | ||||
|         sandbox.stub(window, 'ga'); | ||||
|       }); | ||||
|  | ||||
|       it('sets up user with Amplitude', function() { | ||||
|         analytics.register(); | ||||
|         clock.tick(); | ||||
|         expect(amplitude.setUserId).to.have.been.calledOnce; | ||||
|         expect(amplitude.setUserId).to.have.been.calledWith(user._id); | ||||
|       }); | ||||
|  | ||||
|       it('sets up user with Google Analytics', function() { | ||||
|         analytics.register(); | ||||
|         clock.tick(); | ||||
|         expect(ga).to.have.been.calledOnce; | ||||
|         expect(ga).to.have.been.calledWith('set', {userId: user._id}); | ||||
|       }); | ||||
|     }); | ||||
|  | ||||
|     describe('login', function() { | ||||
|  | ||||
|       beforeEach(function() { | ||||
|         sandbox.stub(amplitude, 'setUserId'); | ||||
|         sandbox.stub(window, 'ga'); | ||||
|       }); | ||||
|  | ||||
|       it('sets up tracking for amplitude', function() { | ||||
|         analytics.login(); | ||||
|         clock.tick(); | ||||
|  | ||||
|         expect(amplitude.setUserId).to.have.been.calledOnce; | ||||
|         expect(amplitude.setUserId).to.have.been.calledWith(user._id); | ||||
|       }); | ||||
|  | ||||
|       it('sets up tracking for google analytics', function() { | ||||
|         analytics.login(); | ||||
|         clock.tick(); | ||||
|  | ||||
|         expect(ga).to.have.been.calledOnce; | ||||
|         expect(ga).to.have.been.calledWith('set', {userId: user._id}); | ||||
|       }); | ||||
|     }); | ||||
|  | ||||
|     describe('track', function() { | ||||
|  | ||||
|       beforeEach(function() { | ||||
|         sandbox.stub(amplitude, 'logEvent'); | ||||
|         sandbox.stub(window, 'ga'); | ||||
|       }); | ||||
|  | ||||
|       context('successful tracking', function() { | ||||
|  | ||||
|         it('tracks a simple user action with Amplitude', function() { | ||||
|           var properties = {'hitType':'event','eventCategory':'behavior','eventAction':'cron'}; | ||||
|           analytics.track(properties); | ||||
|           clock.tick(); | ||||
|  | ||||
|           expect(amplitude.logEvent).to.have.been.calledOnce; | ||||
|           expect(amplitude.logEvent).to.have.been.calledWith('cron', properties); | ||||
|         }); | ||||
|  | ||||
|         it('tracks a simple user action with Google Analytics', function() { | ||||
|           var properties = {'hitType':'event','eventCategory':'behavior','eventAction':'cron'}; | ||||
|           analytics.track(properties); | ||||
|           clock.tick(); | ||||
|  | ||||
|           expect(ga).to.have.been.calledOnce; | ||||
|           expect(ga).to.have.been.calledWith('send', properties); | ||||
|         }); | ||||
|  | ||||
|         it('tracks a user action with additional properties in Amplitude', function() { | ||||
|           var properties = {'hitType':'event','eventCategory':'behavior','eventAction':'cron','booleanProperty':true,'numericProperty':17,'stringProperty':'bagel'}; | ||||
|           analytics.track(properties); | ||||
|           clock.tick(); | ||||
|  | ||||
|           expect(amplitude.logEvent).to.have.been.calledOnce; | ||||
|           expect(amplitude.logEvent).to.have.been.calledWith('cron', properties); | ||||
|         }); | ||||
|  | ||||
|         it('tracks a user action with additional properties in google analytics', function() { | ||||
|           var properties = {'hitType':'event','eventCategory':'behavior','eventAction':'cron','booleanProperty':true,'numericProperty':17,'stringProperty':'bagel'}; | ||||
|           analytics.track(properties); | ||||
|           clock.tick(); | ||||
|  | ||||
|           expect(ga).to.have.been.calledOnce; | ||||
|           expect(ga).to.have.been.calledWith('send', properties); | ||||
|         }); | ||||
|       }); | ||||
|  | ||||
|       context('unsuccessful tracking', function() { | ||||
|  | ||||
|         beforeEach(function() { | ||||
|           sandbox.stub(console, 'log'); | ||||
|         }); | ||||
|  | ||||
|         context('events without required properties', function() { | ||||
|           beforeEach(function(){ | ||||
|             analytics.track('action'); | ||||
|             analytics.track({'hitType':'pageview','eventCategory':'green'}); | ||||
|             analytics.track({'hitType':'pageview','eventAction':'eat'}); | ||||
|             analytics.track({'eventCategory':'green','eventAction':'eat'}); | ||||
|             analytics.track({'hitType':'pageview'}); | ||||
|             analytics.track({'eventCategory':'green'}); | ||||
|             analytics.track({'eventAction':'eat'}); | ||||
|             clock.tick(); | ||||
|           }); | ||||
|  | ||||
|           it('logs errors to console', function() { | ||||
|             expect(console.log.callCount).to.eql(7); | ||||
|           }); | ||||
|  | ||||
|           it('does not call out to Amplitude', function() { | ||||
|             expect(amplitude.logEvent).to.not.be.called; | ||||
|           }); | ||||
|  | ||||
|           it('does not call out to Google Analytics', function() { | ||||
|             expect(ga).to.not.be.called; | ||||
|           }); | ||||
|         }); | ||||
|  | ||||
|         context('incorrect hit type', function() { | ||||
|           beforeEach(function() { | ||||
|             analytics.track({'hitType':'moogly','eventCategory':'green','eventAction':'eat'}); | ||||
|             clock.tick(); | ||||
|           }); | ||||
|  | ||||
|           it('logs error to console', function () { | ||||
|             expect(console.log).to.have.been.calledOnce; | ||||
|           }); | ||||
|  | ||||
|           it('does not call out to Amplitude', function() { | ||||
|             expect(amplitude.logEvent).to.not.be.called; | ||||
|           }); | ||||
|  | ||||
|           it('does not call out to Google Analytics', function() { | ||||
|             expect(ga).to.not.be.called; | ||||
|           }); | ||||
|         }); | ||||
|       }); | ||||
|     }); | ||||
|  | ||||
|     describe('updateUser', function() { | ||||
|  | ||||
|       beforeEach(function() { | ||||
|         sandbox.stub(amplitude, 'setUserProperties'); | ||||
|         sandbox.stub(window, 'ga'); | ||||
|       }); | ||||
|  | ||||
|       context('properties argument provided', function(){ | ||||
|         var properties = {'userBoolean': false, 'userNumber': -8, 'userString': 'Enlightened'}; | ||||
|         var expectedProperties = _.cloneDeep(properties); | ||||
|         expectedProperties.UUID = 'unique-user-id'; | ||||
|         expectedProperties.Class = 'wizard'; | ||||
|         expectedProperties.Experience = 35; | ||||
|         expectedProperties.Gold = 43; | ||||
|         expectedProperties.Health = 48; | ||||
|         expectedProperties.Level = 24; | ||||
|         expectedProperties.Mana = 41; | ||||
|         expectedProperties.tutorialComplete = false; | ||||
|         expectedProperties["Number Of Tasks"] = { | ||||
|           habits: 1, | ||||
|           dailys: 1, | ||||
|           todos: 1, | ||||
|           rewards: 1 | ||||
|         }; | ||||
|         expectedProperties.balance = 12; | ||||
|         expectedProperties.balanceGemAmount = 48; | ||||
|  | ||||
|         beforeEach(function() { | ||||
|           user._id = 'unique-user-id'; | ||||
|           user.stats.class = 'wizard'; | ||||
|           user.stats.exp = 35.7; | ||||
|           user.stats.gp = 43.2; | ||||
|           user.stats.hp = 47.8; | ||||
|           user.stats.lvl = 24; | ||||
|           user.stats.mp = 41; | ||||
|           user.flags.tour.intro = 3; | ||||
|           user.habits = [{_id: 'habit'}]; | ||||
|           user.dailys = [{_id: 'daily'}]; | ||||
|           user.todos = [{_id: 'todo'}]; | ||||
|           user.rewards = [{_id: 'reward'}]; | ||||
|           user.balance = 12; | ||||
|  | ||||
|           analytics.updateUser(properties); | ||||
|           clock.tick(); | ||||
|         }); | ||||
|  | ||||
|         it('calls Amplitude with provided properties and select user info', function() { | ||||
|           expect(amplitude.setUserProperties).to.have.been.calledOnce; | ||||
|           expect(amplitude.setUserProperties).to.have.been.calledWith(expectedProperties); | ||||
|         }); | ||||
|  | ||||
|         it('calls Google Analytics with provided properties and select user info', function() { | ||||
|           expect(ga).to.have.been.calledOnce; | ||||
|           expect(ga).to.have.been.calledWith('set', expectedProperties); | ||||
|         }); | ||||
|       }); | ||||
|  | ||||
|       context('no properties argument provided', function() { | ||||
|         var expectedProperties = { | ||||
|           UUID: 'unique-user-id', | ||||
|           Class: 'wizard', | ||||
|           Experience: 35, | ||||
|           Gold: 43, | ||||
|           Health: 48, | ||||
|           Level: 24, | ||||
|           Mana: 41, | ||||
|           contributorLevel: 1, | ||||
|           subscription: 'unique-plan-id', | ||||
|           tutorialComplete: true, | ||||
|           "Number Of Tasks": { | ||||
|             todos: 1, | ||||
|             dailys: 1, | ||||
|             habits: 1, | ||||
|             rewards: 1 | ||||
|           }, | ||||
|           balance: 12, | ||||
|           balanceGemAmount: 48 | ||||
|         }; | ||||
|  | ||||
|         beforeEach(function() { | ||||
|           user._id = 'unique-user-id'; | ||||
|           user.stats.class = 'wizard'; | ||||
|           user.stats.exp = 35.7; | ||||
|           user.stats.gp = 43.2; | ||||
|           user.stats.hp = 47.8; | ||||
|           user.stats.lvl = 24; | ||||
|           user.stats.mp = 41; | ||||
|           user.contributor.level = 1; | ||||
|           user.purchased.plan.planId = 'unique-plan-id'; | ||||
|           user.flags.tour.intro = -2; | ||||
|           user.habits = [{_id: 'habit'}]; | ||||
|           user.dailys = [{_id: 'daily'}]; | ||||
|           user.todos = [{_id: 'todo'}]; | ||||
|           user.rewards = [{_id: 'reward'}]; | ||||
|           user.balance = 12; | ||||
|  | ||||
|           analytics.updateUser(); | ||||
|           clock.tick(); | ||||
|         }); | ||||
|  | ||||
|         it('calls Amplitude with select user info', function() { | ||||
|           expect(amplitude.setUserProperties).to.have.been.calledOnce; | ||||
|           expect(amplitude.setUserProperties).to.have.been.calledWith(expectedProperties); | ||||
|         }); | ||||
|  | ||||
|         it('calls Google Analytics with select user info', function() { | ||||
|           expect(ga).to.have.been.calledOnce; | ||||
|           expect(ga).to.have.been.calledWith('set', expectedProperties); | ||||
|         }); | ||||
|       }); | ||||
|     }); | ||||
|   }); | ||||
| }); | ||||
| @@ -1,88 +0,0 @@ | ||||
| 'use strict'; | ||||
|  | ||||
| describe('challengeServices', function() { | ||||
|   var $httpBackend, $http, challenges, user; | ||||
|   var apiV3Prefix = '/api/v3'; | ||||
|  | ||||
|   beforeEach(function() { | ||||
|     module(function($provide) { | ||||
|       $provide.value('User', {user:user}); | ||||
|     }); | ||||
|  | ||||
|     inject(function(_$httpBackend_, Challenges, User) { | ||||
|       $httpBackend = _$httpBackend_; | ||||
|       challenges = Challenges; | ||||
|       user = User; | ||||
|       user.sync = function(){}; | ||||
|     }); | ||||
|   }); | ||||
|  | ||||
|   it('calls create challenge endpoint', function() { | ||||
|     $httpBackend.expectPOST(apiV3Prefix + '/challenges').respond({}); | ||||
|     challenges.createChallenge(); | ||||
|     $httpBackend.flush(); | ||||
|   }); | ||||
|  | ||||
|   it('calls join challenge endpoint', function() { | ||||
|     var challengeId = 1; | ||||
|     $httpBackend.expectPOST(apiV3Prefix + '/challenges/' + challengeId + '/join').respond({}); | ||||
|     challenges.joinChallenge(challengeId); | ||||
|     $httpBackend.flush(); | ||||
|   }); | ||||
|  | ||||
|   it('calls leave challenge endpoint', function() { | ||||
|     var challengeId = 1; | ||||
|     $httpBackend.expectPOST(apiV3Prefix + '/challenges/' + challengeId + '/leave').respond({}); | ||||
|     challenges.leaveChallenge(challengeId); | ||||
|     $httpBackend.flush(); | ||||
|   }); | ||||
|  | ||||
|   it('calls get user challenges endpoint', function() { | ||||
|     $httpBackend.expectGET(apiV3Prefix + '/challenges/user').respond({}); | ||||
|     challenges.getUserChallenges(); | ||||
|     $httpBackend.flush(); | ||||
|   }); | ||||
|  | ||||
|   it('calls get group challenges endpoint', function() { | ||||
|     var groupId = 1; | ||||
|     $httpBackend.expectGET(apiV3Prefix + '/challenges/groups/' + groupId).respond({}); | ||||
|     challenges.getGroupChallenges(groupId); | ||||
|     $httpBackend.flush(); | ||||
|   }); | ||||
|  | ||||
|   it('calls get challenge endpoint', function() { | ||||
|     var challengeId = 1; | ||||
|     $httpBackend.expectGET(apiV3Prefix + '/challenges/' + challengeId).respond({}); | ||||
|     challenges.getChallenge(challengeId); | ||||
|     $httpBackend.flush(); | ||||
|   }); | ||||
|  | ||||
|   it('calls export challenge to csv endpoint', function() { | ||||
|     var challengeId = 1; | ||||
|     $httpBackend.expectGET(apiV3Prefix + '/challenges/' + challengeId + '/export/csv').respond({}); | ||||
|     challenges.exportChallengeCsv(challengeId); | ||||
|     $httpBackend.flush(); | ||||
|   }); | ||||
|  | ||||
|   it('calls update challenge endpoint', function() { | ||||
|     var challengeId = 1; | ||||
|     $httpBackend.expectPUT(apiV3Prefix + '/challenges/' + challengeId).respond({}); | ||||
|     challenges.updateChallenge(challengeId); | ||||
|     $httpBackend.flush(); | ||||
|   }); | ||||
|  | ||||
|   it('calls delete challenge endpoint', function() { | ||||
|     var challengeId = 1; | ||||
|     $httpBackend.expectDELETE(apiV3Prefix + '/challenges/' + challengeId).respond({}); | ||||
|     challenges.deleteChallenge(challengeId); | ||||
|     $httpBackend.flush(); | ||||
|   }); | ||||
|  | ||||
|   it('calls select challenge winner endpoint', function() { | ||||
|     var challengeId = 1; | ||||
|     var winnerId = 2; | ||||
|     $httpBackend.expectPOST(apiV3Prefix + '/challenges/' + challengeId + '/selectWinner/' + winnerId).respond({}); | ||||
|     challenges.selectChallengeWinner(challengeId, winnerId); | ||||
|     $httpBackend.flush(); | ||||
|   }); | ||||
| }); | ||||
| @@ -1,73 +0,0 @@ | ||||
| 'use strict'; | ||||
|  | ||||
| describe('chatServices', function() { | ||||
|   var $httpBackend, $http, chat, user; | ||||
|   var apiV3Prefix = '/api/v3'; | ||||
|  | ||||
|   beforeEach(function() { | ||||
|     module(function($provide) { | ||||
|       $provide.value('User', {user:user}); | ||||
|     }); | ||||
|  | ||||
|     inject(function(_$httpBackend_, Chat, User) { | ||||
|       $httpBackend = _$httpBackend_; | ||||
|       chat = Chat; | ||||
|       user = User; | ||||
|       user.sync = function(){}; | ||||
|     }); | ||||
|   }); | ||||
|  | ||||
|   it('calls get chat endpoint', function() { | ||||
|     var groupId = 1; | ||||
|     $httpBackend.expectGET(apiV3Prefix + '/groups/' + groupId + '/chat').respond({}); | ||||
|     chat.getChat(groupId); | ||||
|     $httpBackend.flush(); | ||||
|   }); | ||||
|  | ||||
|   it('calls get chat endpoint', function() { | ||||
|     var groupId = 1; | ||||
|     var message = "test message"; | ||||
|     $httpBackend.expectPOST(apiV3Prefix + '/groups/' + groupId + '/chat').respond({}); | ||||
|     chat.postChat(groupId, message); | ||||
|     $httpBackend.flush(); | ||||
|   }); | ||||
|  | ||||
|   it('calls delete chat endpoint', function() { | ||||
|     var groupId = 1; | ||||
|     var chatId = 2; | ||||
|     $httpBackend.expectDELETE(apiV3Prefix + '/groups/' + groupId + '/chat/' + chatId).respond({}); | ||||
|     chat.deleteChat(groupId, chatId); | ||||
|     $httpBackend.flush(); | ||||
|   }); | ||||
|  | ||||
|   it('calls like chat endpoint', function() { | ||||
|     var groupId = 1; | ||||
|     var chatId = 2; | ||||
|     $httpBackend.expectPOST(apiV3Prefix + '/groups/' + groupId + '/chat/' + chatId + '/like').respond({}); | ||||
|     chat.like(groupId, chatId); | ||||
|     $httpBackend.flush(); | ||||
|   }); | ||||
|  | ||||
|   it('calls flag chat endpoint', function() { | ||||
|     var groupId = 1; | ||||
|     var chatId = 2; | ||||
|     $httpBackend.expectPOST(apiV3Prefix + '/groups/' + groupId + '/chat/' + chatId + '/flag').respond({}); | ||||
|     chat.flagChatMessage(groupId, chatId); | ||||
|     $httpBackend.flush(); | ||||
|   }); | ||||
|  | ||||
|   it('calls clearflags chat endpoint', function() { | ||||
|     var groupId = 1; | ||||
|     var chatId = 2; | ||||
|     $httpBackend.expectPOST(apiV3Prefix + '/groups/' + groupId + '/chat/' + chatId + '/clearflags').respond({}); | ||||
|     chat.clearFlagCount(groupId, chatId); | ||||
|     $httpBackend.flush(); | ||||
|   }); | ||||
|  | ||||
|   it('calls chat seen endpoint', function() { | ||||
|     var groupId = 1; | ||||
|     $httpBackend.expectPOST(apiV3Prefix + '/groups/' + groupId + '/chat/seen').respond({}); | ||||
|     chat.markChatSeen(groupId); | ||||
|     $httpBackend.flush(); | ||||
|   }); | ||||
| }); | ||||
| @@ -1,200 +0,0 @@ | ||||
| 'use strict'; | ||||
|  | ||||
| describe('groupServices', function() { | ||||
|   var $httpBackend, $http, groups, user, $rootScope; | ||||
|   var groupApiUrlPrefix = '/api/v3/groups'; | ||||
|  | ||||
|   beforeEach(function() { | ||||
|     module(function($provide) { | ||||
|       user = specHelper.newUser(); | ||||
|       user._id = "unique-user-id" | ||||
|       user.party._id = 'unique-party-id'; | ||||
|       user.sync = function(){}; | ||||
|       $provide.value('User', {user: user}); | ||||
|     }); | ||||
|  | ||||
|     inject(function(_$httpBackend_, _$rootScope_, Groups, User) { | ||||
|       $httpBackend = _$httpBackend_; | ||||
|       $rootScope = _$rootScope_; | ||||
|       $rootScope.openModal = function() {} | ||||
|       groups = Groups; | ||||
|     }); | ||||
|   }); | ||||
|  | ||||
|   it('calls get groups', function() { | ||||
|     $httpBackend.expectGET(groupApiUrlPrefix).respond({}); | ||||
|     groups.Group.getGroups(); | ||||
|     $httpBackend.flush(); | ||||
|   }); | ||||
|  | ||||
|   it('calls get group', function() { | ||||
|     var gid = 1; | ||||
|     $httpBackend.expectGET(groupApiUrlPrefix + '/' + gid).respond({}); | ||||
|     groups.Group.get(gid); | ||||
|     $httpBackend.flush(); | ||||
|   }); | ||||
|  | ||||
|   it('calls party endpoint', function() { | ||||
|     var groupId = '1234'; | ||||
|     var groupResponse = {data: {_id: groupId}}; | ||||
|     $httpBackend.expectGET(groupApiUrlPrefix + '/party').respond(groupResponse); | ||||
|     $httpBackend.expectGET('/api/v3/groups/' + groupId + '/members?includeAllPublicFields=true').respond({}); | ||||
|     $httpBackend.expectGET('/api/v3/groups/' + groupId + '/invites').respond({}); | ||||
|     $httpBackend.expectGET('/api/v3/challenges/groups/' + groupId).respond({}); | ||||
|     groups.Group.syncParty(); | ||||
|     $httpBackend.flush(); | ||||
|   }); | ||||
|  | ||||
|   it('calls create endpoint', function() { | ||||
|     $httpBackend.expectPOST(groupApiUrlPrefix).respond({}); | ||||
|     groups.Group.create({}); | ||||
|     $httpBackend.flush(); | ||||
|   }); | ||||
|  | ||||
|   it('calls update group', function() { | ||||
|     var gid = 1; | ||||
|     var groupDetails = { _id: gid }; | ||||
|     $httpBackend.expectPUT(groupApiUrlPrefix + '/' + gid).respond({}); | ||||
|     groups.Group.update(groupDetails); | ||||
|     $httpBackend.flush(); | ||||
|   }); | ||||
|  | ||||
|   it('calls join group', function() { | ||||
|     var gid = 1; | ||||
|     $httpBackend.expectPOST(groupApiUrlPrefix + '/' + gid + '/join').respond({}); | ||||
|     groups.Group.join(gid); | ||||
|     $httpBackend.flush(); | ||||
|   }); | ||||
|  | ||||
|   it('calls reject invite group', function() { | ||||
|     var gid = 1; | ||||
|     $httpBackend.expectPOST(groupApiUrlPrefix + '/' + gid + '/reject-invite').respond({}); | ||||
|     groups.Group.rejectInvite(gid); | ||||
|     $httpBackend.flush(); | ||||
|   }); | ||||
|  | ||||
|   it('calls invite group', function() { | ||||
|     var gid = 1; | ||||
|     $httpBackend.expectPOST(groupApiUrlPrefix + '/' + gid + '/invite').respond({}); | ||||
|     groups.Group.invite(gid, [], []); | ||||
|     $httpBackend.flush(); | ||||
|   }); | ||||
|  | ||||
|   it('calls party endpoint when party is not cached', function() { | ||||
|     var groupId = '1234'; | ||||
|     var groupResponse = {data: {_id: groupId}}; | ||||
|     $httpBackend.expectGET(groupApiUrlPrefix + '/party').respond(groupResponse); | ||||
|     $httpBackend.expectGET('/api/v3/groups/' + groupId + '/members?includeAllPublicFields=true').respond({}); | ||||
|     $httpBackend.expectGET('/api/v3/groups/' + groupId + '/invites').respond({}); | ||||
|     $httpBackend.expectGET('/api/v3/challenges/groups/' + groupId).respond({}); | ||||
|     groups.party(); | ||||
|     $httpBackend.flush(); | ||||
|   }); | ||||
|  | ||||
|   it('returns party if cached', function (done) { | ||||
|     var uid = 'abc'; | ||||
|     var party = { | ||||
|       _id: uid, | ||||
|     }; | ||||
|     groups.data.party = party; | ||||
|     groups.party() | ||||
|       .then(function (result) { | ||||
|         expect(result).to.eql(party); | ||||
|         done(); | ||||
|       }); | ||||
|     $httpBackend.flush(); | ||||
|   }); | ||||
|  | ||||
|   it('calls tavern endpoint when tavern is not cached', function() { | ||||
|     $httpBackend.expectGET(groupApiUrlPrefix + '/habitrpg').respond({}); | ||||
|     groups.tavern(); | ||||
|     $httpBackend.flush(); | ||||
|   }); | ||||
|  | ||||
|   it('returns tavern if cached', function (done) { | ||||
|     var uid = 'abc'; | ||||
|     var tavern = { | ||||
|       _id: uid, | ||||
|     }; | ||||
|     groups.data.tavern = tavern; | ||||
|     groups.tavern() | ||||
|       .then(function (result) { | ||||
|         expect(result).to.eql(tavern); | ||||
|         done(); | ||||
|       }); | ||||
|     $httpBackend.flush(); | ||||
|   }); | ||||
|  | ||||
|   it('calls public guilds endpoint', function() { | ||||
|     $httpBackend.expectGET(groupApiUrlPrefix + '?type=publicGuilds').respond([]); | ||||
|     groups.publicGuilds(); | ||||
|     $httpBackend.flush(); | ||||
|   }); | ||||
|  | ||||
|   it('returns public guilds if cached', function (done) { | ||||
|     var uid = 'abc'; | ||||
|     var publicGuilds = [ | ||||
|       {_id: uid}, | ||||
|     ]; | ||||
|     groups.data.publicGuilds = publicGuilds; | ||||
|  | ||||
|     groups.publicGuilds() | ||||
|       .then(function (result) { | ||||
|         expect(result).to.eql(publicGuilds); | ||||
|         done(); | ||||
|       }); | ||||
|  | ||||
|     $httpBackend.flush(); | ||||
|   }); | ||||
|  | ||||
|   it('calls my guilds endpoint', function() { | ||||
|     $httpBackend.expectGET(groupApiUrlPrefix + '?type=guilds').respond([]); | ||||
|     groups.myGuilds(); | ||||
|     $httpBackend.flush(); | ||||
|   }); | ||||
|  | ||||
|   it('returns my guilds if cached', function (done) { | ||||
|     var uid = 'abc'; | ||||
|     var myGuilds = [ | ||||
|       {_id: uid}, | ||||
|     ]; | ||||
|     groups.data.myGuilds = myGuilds; | ||||
|  | ||||
|     groups.myGuilds() | ||||
|       .then(function (myGuilds) { | ||||
|         expect(myGuilds).to.eql(myGuilds); | ||||
|         done(); | ||||
|       }); | ||||
|  | ||||
|     $httpBackend.flush() | ||||
|   }); | ||||
|  | ||||
|   it('sets a "sendInviteText" property on a party to "Send Invitations"', function() { | ||||
|     var sendInviteText = window.env.t('sendInvitations'); | ||||
|     var party = { | ||||
|       type: 'party', | ||||
|       data: { | ||||
|         _id: '1234', | ||||
|       }, | ||||
|     }; | ||||
|     groups.inviteOrStartParty(party); | ||||
|     expect(party.sendInviteText).to.eql(sendInviteText); | ||||
|   }); | ||||
|  | ||||
|   it('sets a "sendInviteText" proptery on a guild to "Send Invitations +$3.00/month/user"', function() { | ||||
|     var sendInviteText = window.env.t('sendInvitations'); | ||||
|     var guild = { | ||||
|       type: 'guild', | ||||
|       data: { | ||||
|         _id: '12345', | ||||
|       }, | ||||
|       purchased: { | ||||
|         plan: { | ||||
|           customerId: '123', | ||||
|         }, | ||||
|       }, | ||||
|     }; | ||||
|     groups.inviteOrStartParty(guild); | ||||
|     expect(guild.sendInviteText).to.eql(sendInviteText + window.env.t('groupAdditionalUserCost')); | ||||
|   }); | ||||
| }); | ||||
| @@ -1,118 +0,0 @@ | ||||
| 'use strict'; | ||||
|  | ||||
| describe('memberServices', function() { | ||||
|   var $httpBackend, members; | ||||
|   var apiV3Prefix = '/api/v3'; | ||||
|  | ||||
|   beforeEach(inject(function (_$httpBackend_, Members) { | ||||
|     $httpBackend = _$httpBackend_; | ||||
|     members = Members; | ||||
|   })); | ||||
|  | ||||
|    afterEach(function() { | ||||
|      $httpBackend.verifyNoOutstandingExpectation(); | ||||
|      $httpBackend.verifyNoOutstandingRequest(); | ||||
|    }); | ||||
|  | ||||
|  | ||||
|   it('has no members at the beginning', function() { | ||||
|     expect(members.members).to.be.an('object'); | ||||
|     expect(members.members).to.eql({}); | ||||
|     expect(members.selectedMember).to.be.undefined; | ||||
|   }); | ||||
|  | ||||
|   it('calls fetch member', function() { | ||||
|     var memberId = 1; | ||||
|     var memberUrl = apiV3Prefix + '/members/' + memberId; | ||||
|     $httpBackend.expectGET(memberUrl).respond({}); | ||||
|     members.fetchMember(memberId); | ||||
|     $httpBackend.flush(); | ||||
|   }); | ||||
|  | ||||
|   it('calls get group members', function() { | ||||
|     var groupId = 1; | ||||
|     var memberUrl = apiV3Prefix + '/groups/' + groupId + '/members'; | ||||
|     $httpBackend.expectGET(memberUrl).respond({}); | ||||
|     members.getGroupMembers(groupId); | ||||
|     $httpBackend.flush(); | ||||
|   }); | ||||
|  | ||||
|   it('calls get group invites', function() { | ||||
|     var groupId = 1; | ||||
|     var memberUrl = apiV3Prefix + '/groups/' + groupId + '/invites'; | ||||
|     $httpBackend.expectGET(memberUrl).respond({}); | ||||
|     members.getGroupInvites(groupId); | ||||
|     $httpBackend.flush(); | ||||
|   }); | ||||
|  | ||||
|   it('calls get challenge members', function() { | ||||
|     var challengeId = 1; | ||||
|     var memberUrl = apiV3Prefix + '/challenges/' + challengeId + '/members'; | ||||
|     $httpBackend.expectGET(memberUrl).respond({}); | ||||
|     members.getChallengeMembers(challengeId); | ||||
|     $httpBackend.flush(); | ||||
|   }); | ||||
|  | ||||
|   it('calls get challenge members progress', function() { | ||||
|     var challengeId = 1; | ||||
|     var memberId = 2; | ||||
|     var memberUrl = apiV3Prefix + '/challenges/' + challengeId + '/members/' + memberId; | ||||
|     $httpBackend.expectGET(memberUrl).respond({}); | ||||
|     members.getChallengeMemberProgress(challengeId, memberId); | ||||
|     $httpBackend.flush(); | ||||
|   }); | ||||
|  | ||||
|   describe('addToMembersList', function() { | ||||
|     it('adds member to members object', function() { | ||||
|       var member = { _id: 'user_id' }; | ||||
|       members.addToMembersList(member, members); | ||||
|       expect(members.members).to.eql({ | ||||
|         user_id: { _id: 'user_id' } | ||||
|       }); | ||||
|     }); | ||||
|   }); | ||||
|  | ||||
|   describe('selectMember', function() { | ||||
|     it('fetches member if not already in cache', function(done) { | ||||
|       var uid = 'abc'; | ||||
|       var memberResponse = { | ||||
|         data: {_id: uid}, | ||||
|       } | ||||
|       $httpBackend.expectGET(apiV3Prefix + '/members/' + uid).respond(memberResponse); | ||||
|       members.selectMember(uid) | ||||
|         .then(function () { | ||||
|           expect(members.selectedMember._id).to.eql(uid); | ||||
|           expect(members.members).to.have.property(uid); | ||||
|           done(); | ||||
|         }); | ||||
|       $httpBackend.flush(); | ||||
|     }); | ||||
|  | ||||
|     it('fetches member if member data in cache is incomplete', function(done) { | ||||
|       var uid = 'abc'; | ||||
|       members.members = { | ||||
|         abc: { _id: 'abc', items: {} } | ||||
|       } | ||||
|       var memberResponse = { | ||||
|         data: {_id: uid}, | ||||
|       } | ||||
|       $httpBackend.expectGET(apiV3Prefix + '/members/' + uid).respond(memberResponse); | ||||
|       members.selectMember(uid) | ||||
|         .then(function () { | ||||
|           expect(members.selectedMember._id).to.eql(uid); | ||||
|           expect(members.members).to.have.property(uid); | ||||
|           done(); | ||||
|         }); | ||||
|       $httpBackend.flush(); | ||||
|     }); | ||||
|  | ||||
|     it('gets member from cache if member has a weapons object', function() { | ||||
|       var uid = 'abc'; | ||||
|       members.members[uid] = { _id: uid, items: { weapon: {} } }; | ||||
|       members.selectMember(uid, function(){ | ||||
|         expect(members.selectedMember._id).to.eql(uid); | ||||
|         expect(members.members).to.have.property(uid); | ||||
|       }); | ||||
|     }); | ||||
|   }); | ||||
| }); | ||||
| @@ -1,202 +0,0 @@ | ||||
| 'use strict'; | ||||
|  | ||||
| describe('notificationServices', function() { | ||||
|   var notification; | ||||
|  | ||||
|   beforeEach(function() { | ||||
|     sandbox.stub($, 'pnotify', function(){ | ||||
|       return { click: function(){}} | ||||
|     }); | ||||
|  | ||||
|     module(function($provide){ | ||||
|       $provide.value('User', {}); | ||||
|     }); | ||||
|  | ||||
|     inject(function(Notification) { | ||||
|       notification = Notification; | ||||
|     }); | ||||
|   }); | ||||
|  | ||||
|   it('notifies coins amount', function() { | ||||
|     var SILVER_COIN = "<span class='notification-icon shop_silver'></span>"; | ||||
|     var GOLD_COIN = "<span class='notification-icon shop_gold'></span>"; | ||||
|  | ||||
|     expect(notification.coins(0)).to.not.exist; | ||||
|     expect(notification.coins(0.01)).to.eql("1 " + SILVER_COIN); | ||||
|     expect(notification.coins(0.1)).to.eql("10 " + SILVER_COIN); | ||||
|     expect(notification.coins(1)).to.eql("1 " + GOLD_COIN); | ||||
|     expect(notification.coins(12.34)).to.eql("12 " + GOLD_COIN +" 33 " + SILVER_COIN); | ||||
|   }); | ||||
|  | ||||
|   it('sends crit notification', function() { | ||||
|     notification.crit(5); | ||||
|     var arg = $.pnotify.args[0][0]; | ||||
|  | ||||
|     expect($.pnotify).to.have.been.calledOnce; | ||||
|     expect(arg.type).to.eql('crit'); | ||||
|     expect(arg.text).to.eql('Critical Hit! Bonus: 5%'); | ||||
|     expect(arg.icon).to.eql('glyphicon glyphicon-certificate'); | ||||
|   }); | ||||
|  | ||||
|   it('sends drop notification for unspecified item', function() { | ||||
|     notification.drop('msg'); | ||||
|     var arg = $.pnotify.args[0][0]; | ||||
|  | ||||
|     expect($.pnotify).to.have.been.calledOnce; | ||||
|     expect(arg.type).to.eql('drop'); | ||||
|     expect(arg.text).to.eql('msg'); | ||||
|     expect(arg.icon).to.eql(false); | ||||
|   }); | ||||
|  | ||||
|   it('sends drop notification for Egg', function() { | ||||
|     var item = { type: 'Egg', key: 'wolf' }; | ||||
|     notification.drop('msg', item); | ||||
|     var arg = $.pnotify.args[0][0]; | ||||
|  | ||||
|     expect($.pnotify).to.have.been.calledOnce; | ||||
|     expect(arg.type).to.eql('drop'); | ||||
|     expect(arg.text).to.eql('msg'); | ||||
|     expect(arg.icon).to.eql('Pet_Egg_wolf'); | ||||
|   }); | ||||
|  | ||||
|   it('sends drop notification for Hatching Potion', function() { | ||||
|     var item = { type: 'HatchingPotion', key: 'red' }; | ||||
|     notification.drop('msg', item); | ||||
|     var arg = $.pnotify.args[0][0]; | ||||
|  | ||||
|     expect($.pnotify).to.have.been.calledOnce; | ||||
|     expect(arg.type).to.eql('drop'); | ||||
|     expect(arg.text).to.eql('msg'); | ||||
|     expect(arg.icon).to.eql('Pet_HatchingPotion_red'); | ||||
|   }); | ||||
|  | ||||
|   it('sends drop notification for Food', function() { | ||||
|     var item = { type: 'Food', key: 'meat' }; | ||||
|     notification.drop('msg', item); | ||||
|     var arg = $.pnotify.args[0][0]; | ||||
|  | ||||
|     expect($.pnotify).to.have.been.calledOnce; | ||||
|     expect(arg.type).to.eql('drop'); | ||||
|     expect(arg.text).to.eql('msg'); | ||||
|     expect(arg.icon).to.eql('Pet_Food_meat'); | ||||
|   }); | ||||
|  | ||||
|   it('does not send exp notification if val < -50', function() { | ||||
|     notification.exp(-51); | ||||
|     expect($.pnotify).to.not.have.been.called; | ||||
|   }); | ||||
|  | ||||
|   it('sends exp notification if val >= -50', function() { | ||||
|     notification.exp(50); | ||||
|     notification.exp(0); | ||||
|     notification.exp(-50); | ||||
|     var arg = $.pnotify.args[0][0]; | ||||
|  | ||||
|     expect($.pnotify).to.have.been.calledThrice; | ||||
|     expect(arg.type).to.eql('xp'); | ||||
|     expect(arg.text).to.eql('+ 50 Experience'); | ||||
|     expect(arg.icon).to.eql('glyphicon glyphicon-star'); | ||||
|   }); | ||||
|  | ||||
|   it('sends exp notification with rounded value', function() { | ||||
|     notification.exp(50.23333); | ||||
|     var arg = $.pnotify.args[0][0]; | ||||
|  | ||||
|     expect($.pnotify).to.have.been.calledOnce; | ||||
|     expect(arg.type).to.eql('xp'); | ||||
|     expect(arg.text).to.eql('+ 50.2 Experience'); | ||||
|     expect(arg.icon).to.eql('glyphicon glyphicon-star'); | ||||
|   }); | ||||
|  | ||||
|   it('sends error notification', function() { | ||||
|     notification.error('there was an error'); | ||||
|     var arg = $.pnotify.args[0][0]; | ||||
|  | ||||
|     expect($.pnotify).to.have.been.calledOnce; | ||||
|     expect(arg.type).to.eql('danger'); | ||||
|     expect(arg.text).to.eql('there was an error'); | ||||
|     expect(arg.icon).to.eql('glyphicon glyphicon-exclamation-sign'); | ||||
|   }); | ||||
|  | ||||
|   it('sends gp gained notification', function() { | ||||
|     notification.gp(50, 4); | ||||
|     var arg = $.pnotify.args[0][0]; | ||||
|  | ||||
|     expect($.pnotify).to.have.been.calledOnce; | ||||
|     expect(arg.type).to.eql('gp'); | ||||
|     expect(arg.text).to.eql('+ 46 <span class=\'notification-icon shop_gold\'></span>'); | ||||
|     expect(arg.icon).to.eql(false); | ||||
|   }); | ||||
|  | ||||
|   it('sends hp notification', function() { | ||||
|     notification.hp(10); | ||||
|     var arg = $.pnotify.args[0][0]; | ||||
|  | ||||
|     expect($.pnotify).to.have.been.calledOnce; | ||||
|     expect(arg.type).to.eql('hp'); | ||||
|     expect(arg.text).to.eql('+ 10 Health'); | ||||
|     expect(arg.icon).to.eql('glyphicon glyphicon-heart'); | ||||
|   }); | ||||
|  | ||||
|   it('sends level up notification', function() { | ||||
|     notification.lvl(10); | ||||
|     var arg = $.pnotify.args[0][0]; | ||||
|  | ||||
|     expect($.pnotify).to.have.been.calledOnce; | ||||
|     expect(arg.type).to.eql('lvl'); | ||||
|     expect(arg.text).to.eql('Level Up!'); | ||||
|     expect(arg.icon).to.eql('glyphicon glyphicon-chevron-up'); | ||||
|   }); | ||||
|  | ||||
|   it('sends markdown parsed notification', function() { | ||||
|     notification.markdown(":smile: - task name"); | ||||
|     var arg = $.pnotify.args[0][0]; | ||||
|  | ||||
|     expect($.pnotify).to.have.been.calledOnce; | ||||
|     expect(arg.type).to.eql('info'); | ||||
|     expect(arg.text).to.eql('<p><img class="habitica-emoji" style="height: 1.5em; width: 1.5em" src="https://s3.amazonaws.com/habitica-assets/cdn/emoji/smile.png" alt="smile"> - task name</p>\n'); | ||||
|     expect(arg.icon).to.eql(false); | ||||
|   }); | ||||
|  | ||||
|   it('does not send markdown notification if no text is given', function() { | ||||
|     notification.markdown(); | ||||
|  | ||||
|     expect($.pnotify).to.not.have.been.called; | ||||
|   }); | ||||
|  | ||||
|   it('sends mp notification', function() { | ||||
|     notification.mp(10); | ||||
|     var arg = $.pnotify.args[0][0]; | ||||
|  | ||||
|     expect($.pnotify).to.have.been.calledOnce; | ||||
|     expect(arg.type).to.eql('mp'); | ||||
|     expect(arg.text).to.eql('+ 10 Mana'); | ||||
|     expect(arg.icon).to.eql('glyphicon glyphicon-fire'); | ||||
|   }); | ||||
|  | ||||
|   it('sends streak notification', function() { | ||||
|     notification.streak(10); | ||||
|     var arg = $.pnotify.args[0][0]; | ||||
|  | ||||
|     expect($.pnotify).to.have.been.calledOnce; | ||||
|     expect(arg.type).to.eql('streak'); | ||||
|     expect(arg.text).to.eql('Streak Achievements: 10'); | ||||
|     expect(arg.icon).to.eql('glyphicon glyphicon-repeat'); | ||||
|   }); | ||||
|  | ||||
|   it('sends text notification', function() { | ||||
|     notification.text('task name'); | ||||
|     var arg = $.pnotify.args[0][0]; | ||||
|  | ||||
|     expect($.pnotify).to.have.been.calledOnce; | ||||
|     expect(arg.type).to.eql('info'); | ||||
|     expect(arg.text).to.eql('task name'); | ||||
|     expect(arg.icon).to.eql(false); | ||||
|   }); | ||||
|  | ||||
|   it('does not send text notification if no text is given', function() { | ||||
|     notification.text(); | ||||
|  | ||||
|     expect($.pnotify).to.not.have.been.called; | ||||
|   }); | ||||
| }); | ||||
| @@ -1,435 +0,0 @@ | ||||
| 'use strict'; | ||||
|  | ||||
| describe('Quests Service', function() { | ||||
|   var groupsService, quest, questsService, user, content, resolveSpy, rejectSpy, state; | ||||
|  | ||||
|   beforeEach(function() { | ||||
|     user = specHelper.newUser(); | ||||
|     user.ops = { | ||||
|       buyQuest: sandbox.spy() | ||||
|     }; | ||||
|     user.party._id = 'unique-party-id'; | ||||
|  | ||||
|     user.achievements.quests = {}; | ||||
|     quest = {lvl:20}; | ||||
|  | ||||
|     module(function($provide) { | ||||
|       $provide.value('User', {sync: sinon.stub(), user: user}); | ||||
|     }); | ||||
|  | ||||
|     inject(function(Quests, Groups, Content, _$state_) { | ||||
|       questsService = Quests; | ||||
|       groupsService = Groups; | ||||
|       content = Content; | ||||
|       state = _$state_; | ||||
|     }); | ||||
|  | ||||
|     sandbox.stub(groupsService, 'inviteOrStartParty'); | ||||
|     sandbox.stub(window,'confirm'); | ||||
|     sandbox.stub(window,'alert'); | ||||
|     resolveSpy = sandbox.spy(); | ||||
|     rejectSpy = sandbox.spy(); | ||||
|   }); | ||||
|  | ||||
|   describe('#lockQuest', function() { | ||||
|  | ||||
|     it('locks quest when user does not meet level requirement', function() { | ||||
|       user.stats.lvl = 15; | ||||
|  | ||||
|       expect(questsService.lockQuest(quest)).to.be.ok; | ||||
|     }); | ||||
|  | ||||
|     it('does not lock quest if we ignore level requirement', function() { | ||||
|       user.stats.lvl = 15; | ||||
|  | ||||
|       expect(questsService.lockQuest(quest,true)).to.not.be.ok; | ||||
|     }); | ||||
|  | ||||
|     it('does not lock quest if user meets level requirement', function() { | ||||
|       user.stats.lvl = 20; | ||||
|  | ||||
|       expect(questsService.lockQuest(quest)).to.not.be.ok; | ||||
|     }); | ||||
|  | ||||
|     it('locks quest if user has not completed previous quest in series', function() { | ||||
|       quest.previous = 'priorQuest'; | ||||
|       user.stats.lvl = 25; | ||||
|  | ||||
|       expect(questsService.lockQuest(quest)).to.be.ok; | ||||
|     }); | ||||
|  | ||||
|     it('does not lock quest if user has completed previous quest in series', function() { | ||||
|       quest.previous = 'priorQuest'; | ||||
|       user.stats.lvl = 25; | ||||
|       user.achievements.quests.priorQuest = 1; | ||||
|  | ||||
|       expect(questsService.lockQuest(quest)).to.not.be.ok; | ||||
|     }); | ||||
|   }); | ||||
|  | ||||
|   describe('#buyQuest', function() { | ||||
|     var scope; | ||||
|  | ||||
|     beforeEach(inject(function($rootScope) { | ||||
|       scope = $rootScope.$new(); | ||||
|     })); | ||||
|  | ||||
|     //@TODO: This is fixed in a Quest Service PR port | ||||
|     xit('returns a promise', function() { | ||||
|       var promise = questsService.buyQuest('whale'); | ||||
|       expect(promise).to.respondTo('then'); | ||||
|     }); | ||||
|  | ||||
|     context('Quest key does not exist', function() { | ||||
|       it('rejects with message that quest is not found', function(done) { | ||||
|         questsService.buyQuest('foo') | ||||
|           .then(resolveSpy, function(rej) { | ||||
|             expect(rej).to.eql('No quest with that key found'); | ||||
|             expect(resolveSpy).to.not.be.called; | ||||
|             done(); | ||||
|         }); | ||||
|  | ||||
|         scope.$apply(); | ||||
|       }); | ||||
|     }); | ||||
|  | ||||
|     context('invite friends', function() { | ||||
|       it('prompts user to invite friends to party for invite reward quests', function() { | ||||
|         questsService.buyQuest('basilist'); | ||||
|  | ||||
|         expect(window.confirm).to.be.calledOnce; | ||||
|         expect(window.confirm).to.be.calledWith(env.t('mustInviteFriend')); | ||||
|       }); | ||||
|  | ||||
|       it('rejects promise if confirm is cancelled', function(done) { | ||||
|         window.confirm.returns(false); | ||||
|  | ||||
|         questsService.buyQuest('basilist') | ||||
|           .then(resolveSpy, function(rej) { | ||||
|             expect(rej).to.eql('Did not want to invite friends'); | ||||
|             expect(window.confirm).to.be.calledOnce; | ||||
|             expect(groupsService.inviteOrStartParty).to.not.be.called; | ||||
|             done(); | ||||
|           }); | ||||
|  | ||||
|         scope.$apply(); | ||||
|       }); | ||||
|  | ||||
|       it('rejects promise if confirm is cofirmed and calls groups service', function(done) { | ||||
|         window.confirm.returns(true); | ||||
|  | ||||
|         questsService.buyQuest('basilist') | ||||
|           .then(resolveSpy, function(rej) { | ||||
|             expect(rej).to.eql('Invite or start party'); | ||||
|             expect(window.confirm).to.be.calledOnce; | ||||
|             expect(groupsService.inviteOrStartParty).to.be.calledOnce; | ||||
|             done(); | ||||
|           }); | ||||
|  | ||||
|         scope.$apply(); | ||||
|       }); | ||||
|     }); | ||||
|  | ||||
|     context('quests in a series', function() { | ||||
|       it('does not allow user to buy subsquent quests in a series if user has no quest achievements', function(done) { | ||||
|         user.stats.lvl = 100; | ||||
|         user.achievements.quests = undefined; | ||||
|  | ||||
|         questsService.buyQuest('goldenknight2') | ||||
|           .then(resolveSpy, function(res) { | ||||
|             expect(window.alert).to.have.been.calledOnce; | ||||
|             expect(res).to.eql('unlockByQuesting'); | ||||
|             expect(resolveSpy).to.not.be.called; | ||||
|             done(); | ||||
|           }); | ||||
|  | ||||
|         scope.$apply(); | ||||
|       }); | ||||
|  | ||||
|       it('does not allow user to buy quests whose previous quests are incomplete', function(done) { | ||||
|         user.stats.lvl = 100; | ||||
|         user.achievements.quests = { | ||||
|           'atom1': 1 | ||||
|         }; | ||||
|  | ||||
|         questsService.buyQuest('goldenknight2') | ||||
|           .then(resolveSpy, function(res) { | ||||
|             expect(window.alert).to.have.been.calledOnce; | ||||
|             expect(resolveSpy).to.not.be.called; | ||||
|             done(); | ||||
|           }); | ||||
|  | ||||
|         scope.$apply(); | ||||
|       }); | ||||
|     }); | ||||
|  | ||||
|     context('quests with level requirement', function() { | ||||
|       it('does not allow user to buy quests beyond their level', function(done) { | ||||
|         user.stats.lvl = 1; | ||||
|  | ||||
|         questsService.buyQuest('vice1') | ||||
|           .then(resolveSpy, function(res) { | ||||
|             expect(window.alert).to.have.been.calledOnce; | ||||
|             expect(res).to.eql('mustLvlQuest'); | ||||
|             done(); | ||||
|           }); | ||||
|  | ||||
|         scope.$apply(); | ||||
|       }); | ||||
|  | ||||
|       it('allows user to buy quest if they meet level requirement', function(done) { | ||||
|         user.stats.lvl = 30; | ||||
|  | ||||
|         questsService.buyQuest('vice1') | ||||
|           .then(function(res) { | ||||
|             expect(res).to.eql(content.quests.vice1); | ||||
|             expect(window.alert).to.not.be.called; | ||||
|             expect(rejectSpy).to.not.be.called; | ||||
|             done(); | ||||
|           }, rejectSpy); | ||||
|  | ||||
|         scope.$apply(); | ||||
|       }); | ||||
|     }); | ||||
|  | ||||
|     context('gold purchasable quests', function() { | ||||
|       it('sends quest object', function(done) { | ||||
|         questsService.buyQuest('dilatoryDistress1') | ||||
|           .then(function(res) { | ||||
|             expect(res).to.eql(content.quests.dilatoryDistress1); | ||||
|             expect(window.alert).to.not.be.called; | ||||
|             expect(rejectSpy).to.not.be.called; | ||||
|             done(); | ||||
|           }, rejectSpy); | ||||
|  | ||||
|         scope.$apply(); | ||||
|       }); | ||||
|     }); | ||||
|  | ||||
|     context('quest bundles', function() { | ||||
|       it('sends bundle object', function(done) { | ||||
|         questsService.buyQuest('featheredFriends') | ||||
|           .then(function(res) { | ||||
|             expect(res).to.eql(content.bundles.featheredFriends); | ||||
|             expect(window.alert).to.not.be.called; | ||||
|             expect(rejectSpy).to.not.be.called; | ||||
|             done(); | ||||
|           }, rejectSpy); | ||||
|  | ||||
|         scope.$apply(); | ||||
|       }); | ||||
|     }); | ||||
|  | ||||
|     context('all other quests', function() { | ||||
|       it('sends quest object', function(done) { | ||||
|         questsService.buyQuest('whale') | ||||
|           .then(function(res) { | ||||
|             expect(res).to.eql(content.quests.whale); | ||||
|             expect(window.alert).to.not.be.called; | ||||
|             expect(rejectSpy).to.not.be.called; | ||||
|             done(); | ||||
|           }, rejectSpy); | ||||
|  | ||||
|         scope.$apply(); | ||||
|       }); | ||||
|     }); | ||||
|   }); | ||||
|  | ||||
|   describe('#showQuest', function() { | ||||
|     var scope; | ||||
|  | ||||
|     beforeEach(inject(function($rootScope) { | ||||
|       scope = $rootScope.$new(); | ||||
|     })); | ||||
|  | ||||
|     xit('returns a promise', function() { | ||||
|       var promise = questsService.showQuest('whale'); | ||||
|       expect(promise).to.respondTo('then'); | ||||
|     }); | ||||
|  | ||||
|     context('Quest key does not exist', function() { | ||||
|       it('rejects with message that quest is not found', function(done) { | ||||
|         questsService.showQuest('foo') | ||||
|           .then(resolveSpy, function(rej) { | ||||
|             expect(rej).to.eql('No quest with that key found'); | ||||
|             expect(resolveSpy).to.not.be.called; | ||||
|             done(); | ||||
|         }); | ||||
|  | ||||
|         scope.$apply(); | ||||
|       }); | ||||
|     }); | ||||
|  | ||||
|     context('quests in a series', function() { | ||||
|       it('does not allow user to buy subsquent quests in a series if user has no quest achievements', function(done) { | ||||
|         user.stats.lvl = 100; | ||||
|         user.achievements.quests = undefined; | ||||
|  | ||||
|         questsService.showQuest('goldenknight2') | ||||
|           .then(resolveSpy, function(res) { | ||||
|             expect(window.alert).to.have.been.calledOnce; | ||||
|             expect(res).to.eql('unlockByQuesting'); | ||||
|             expect(resolveSpy).to.not.be.called; | ||||
|             done(); | ||||
|           }); | ||||
|  | ||||
|         scope.$apply(); | ||||
|       }); | ||||
|  | ||||
|       it('does not allow user to buy quests whose previous quests are incomplete', function(done) { | ||||
|         user.stats.lvl = 100; | ||||
|         user.achievements.quests = { | ||||
|           'atom1': 1 | ||||
|         }; | ||||
|  | ||||
|         questsService.showQuest('goldenknight2') | ||||
|           .then(resolveSpy, function(res) { | ||||
|             expect(window.alert).to.have.been.calledOnce; | ||||
|             expect(resolveSpy).to.not.be.called; | ||||
|             done(); | ||||
|           }); | ||||
|  | ||||
|         scope.$apply(); | ||||
|       }); | ||||
|     }); | ||||
|  | ||||
|     context('quests with level requirement', function() { | ||||
|       it('does not allow user to buy quests beyond their level', function(done) { | ||||
|         user.stats.lvl = 1; | ||||
|  | ||||
|         questsService.showQuest('vice1') | ||||
|           .then(resolveSpy, function(res) { | ||||
|             expect(window.alert).to.have.been.calledOnce; | ||||
|             expect(res).to.eql('mustLvlQuest'); | ||||
|             done(); | ||||
|           }); | ||||
|  | ||||
|         scope.$apply(); | ||||
|       }); | ||||
|  | ||||
|       it('allows user to buy quest if they meet level requirement', function(done) { | ||||
|         user.stats.lvl = 30; | ||||
|  | ||||
|         questsService.showQuest('vice1') | ||||
|           .then(function(res) { | ||||
|             expect(res).to.eql(content.quests.vice1); | ||||
|             expect(window.alert).to.not.be.called; | ||||
|             expect(rejectSpy).to.not.be.called; | ||||
|             done(); | ||||
|           }, rejectSpy); | ||||
|  | ||||
|         scope.$apply(); | ||||
|       }); | ||||
|     }); | ||||
|  | ||||
|     context('gold purchasable quests', function() { | ||||
|       it('sends quest object', function(done) { | ||||
|         questsService.showQuest('dilatoryDistress1') | ||||
|           .then(function(res) { | ||||
|             expect(res).to.eql(content.quests.dilatoryDistress1); | ||||
|             expect(window.alert).to.not.be.called; | ||||
|             expect(rejectSpy).to.not.be.called; | ||||
|             done(); | ||||
|           }, rejectSpy); | ||||
|  | ||||
|         scope.$apply(); | ||||
|       }); | ||||
|     }); | ||||
|  | ||||
|     context('all other quests', function() { | ||||
|       it('sends quest object', function(done) { | ||||
|         questsService.showQuest('whale') | ||||
|           .then(function(res) { | ||||
|             expect(res).to.eql(content.quests.whale); | ||||
|             expect(window.alert).to.not.be.called; | ||||
|             expect(rejectSpy).to.not.be.called; | ||||
|             done(); | ||||
|           }, rejectSpy); | ||||
|  | ||||
|         scope.$apply(); | ||||
|       }); | ||||
|     }); | ||||
|   }); | ||||
|  | ||||
|   describe('#initQuest', function() { | ||||
|     var fakeBackend, scope, key = 'whale'; | ||||
|  | ||||
|     beforeEach(inject(function($httpBackend, $rootScope) { | ||||
|       scope = $rootScope.$new(); | ||||
|       fakeBackend = $httpBackend; | ||||
|       var partyResponse = {data:{_id: 'party-id'}}; | ||||
|  | ||||
|       fakeBackend.when('GET', 'partials/main.html').respond({}); | ||||
|       fakeBackend.when('GET', 'partials/main.html').respond({}); | ||||
|       fakeBackend.when('GET', '/api/v3/groups/party').respond(partyResponse); | ||||
|       fakeBackend.when('GET', '/api/v3/groups/party-id/members?includeAllPublicFields=true').respond({}); | ||||
|       fakeBackend.when('GET', '/api/v3/groups/party-id/invites').respond({}); | ||||
|       fakeBackend.when('GET', '/api/v3/challenges/groups/party-id').respond({}); | ||||
|       fakeBackend.when('POST', '/api/v3/groups/party-id/quests/invite/' + key).respond({quest: { key: 'whale' } }); | ||||
|       fakeBackend.flush(); | ||||
|     })); | ||||
|  | ||||
|     it('returns a promise', function() { | ||||
|       var promise = questsService.initQuest(key); | ||||
|       expect(promise).to.respondTo('then'); | ||||
|     }); | ||||
|  | ||||
|     it('starts a quest', function(done) { | ||||
|       fakeBackend.expectPOST( '/api/v3/groups/party-id/quests/invite/' + key); | ||||
|  | ||||
|       questsService.initQuest(key) | ||||
|         .then(function(res) { | ||||
|           done(); | ||||
|         }); | ||||
|  | ||||
|       fakeBackend.flush(); | ||||
|       scope.$apply(); | ||||
|     }); | ||||
|  | ||||
|     it('brings user to party page'); | ||||
|   }); | ||||
|  | ||||
|   //@TODO: This is fixed in a Quest Service PR port | ||||
|   xdescribe('#sendAction', function() { | ||||
|     var fakeBackend, scope; | ||||
|  | ||||
|     beforeEach(inject(function($httpBackend, $rootScope) { | ||||
|       scope = $rootScope.$new(); | ||||
|       fakeBackend = $httpBackend; | ||||
|       var partyResponse = {data:{_id: 'party-id'}}; | ||||
|  | ||||
|       fakeBackend.when('GET', 'partials/main.html').respond({}); | ||||
|       fakeBackend.when('GET', '/api/v3/groups/party').respond(partyResponse); | ||||
|       fakeBackend.when('POST', '/api/v3/groups/party-id/quests/reject').respond({quest: { key: 'whale' } }); | ||||
|       fakeBackend.flush(); | ||||
|     })); | ||||
|  | ||||
|     it('returns a promise', function() { | ||||
|       var promise = questsService.sendAction('quests/reject'); | ||||
|       expect(promise).to.respondTo('then'); | ||||
|     }); | ||||
|  | ||||
|     it('calls specified quest endpoint', function(done) { | ||||
|       fakeBackend.expectPOST('/api/v3/groups/party-id/quests/reject'); | ||||
|  | ||||
|       questsService.sendAction('quests/reject') | ||||
|         .then(function(res) { | ||||
|           expect(res.key).to.eql('whale'); | ||||
|           done(); | ||||
|         }); | ||||
|  | ||||
|       fakeBackend.flush(); | ||||
|       scope.$apply(); | ||||
|     }); | ||||
|  | ||||
|     it('syncs User', function() { | ||||
|       questsService.sendAction('quests/reject') | ||||
|         .then(function(res) { | ||||
|           expect(User.sync).to.be.calledOnce; | ||||
|           done(); | ||||
|         }); | ||||
|  | ||||
|       scope.$apply(); | ||||
|     }); | ||||
|   }); | ||||
| }); | ||||
| @@ -1,258 +0,0 @@ | ||||
| 'use strict'; | ||||
|  | ||||
| describe('Stats Service', function() { | ||||
|   var scope, statCalc, user; | ||||
|  | ||||
|   beforeEach(function() { | ||||
|     user = specHelper.newUser(); | ||||
|  | ||||
|     module(function($provide) { | ||||
|       $provide.value('User', {user: user}); | ||||
|     }); | ||||
|  | ||||
|     inject(function($rootScope, $controller, Stats) { | ||||
|       statCalc = Stats; | ||||
|     }); | ||||
|   }); | ||||
|  | ||||
|   describe('beastMasterProgress', function() { | ||||
|     it('counts drop pets that user has', function() { | ||||
|       user.items.pets = { | ||||
|         "BearCub-Base" : 5, | ||||
|         "BearCub-CottonCandyBlue" : 5, | ||||
|         "Cactus-Zombie" : 5, | ||||
|         "Deer-Golden" : 5, | ||||
|         "Deer-Red" : 5, | ||||
|         "Egg-Desert" : 5, | ||||
|         "MantisShrimp-Base" : 5, | ||||
|         "Wolf-Spooky": 5 | ||||
|       } | ||||
|  | ||||
|       var beastMasterDisplay = statCalc.beastMasterProgress(user.items.pets); | ||||
|  | ||||
|       expect(beastMasterDisplay).to.eql('3/90'); | ||||
|     }); | ||||
|  | ||||
|     it('counts drop pets with a value of -1', function() { | ||||
|       user.items.pets = { | ||||
|         "BearCub-Base" : -1, | ||||
|         "BearCub-CottonCandyBlue" : -1, | ||||
|         "Cactus-Zombie" : 5, | ||||
|         "Deer-Golden" : 5, | ||||
|         "Deer-Red" : -1, | ||||
|         "Egg-Desert" : 5, | ||||
|         "MantisShrimp-Base" : 5, | ||||
|         "Wolf-Spooky": -1 | ||||
|       } | ||||
|  | ||||
|       var beastMasterDisplay = statCalc.beastMasterProgress(user.items.pets); | ||||
|  | ||||
|       expect(beastMasterDisplay).to.eql('3/90'); | ||||
|     }); | ||||
|  | ||||
|     it('does not count drop pets with a value of 0', function() { | ||||
|       user.items.pets = { | ||||
|         "BearCub-Base" : 0, | ||||
|         "BearCub-CottonCandyBlue" : 0, | ||||
|         "Cactus-Zombie" : 5, | ||||
|         "Deer-Golden" : 5, | ||||
|         "Deer-Red" : 5, | ||||
|         "Egg-Desert" : 5, | ||||
|         "MantisShrimp-Base" : 5 | ||||
|       } | ||||
|  | ||||
|       var beastMasterDisplay = statCalc.beastMasterProgress(user.items.pets); | ||||
|  | ||||
|       expect(beastMasterDisplay).to.eql('1/90'); | ||||
|     }); | ||||
|   }); | ||||
|  | ||||
|   describe('expDisplay', function() { | ||||
|     it('displays exp as "exp / toNextLevelExp"', function() { | ||||
|       user.stats.exp = 10; | ||||
|       user.stats.lvl = 29; | ||||
|       var expDisplay = statCalc.expDisplay(user); | ||||
|  | ||||
|       expect(expDisplay).to.eql('10/640'); | ||||
|     }); | ||||
|  | ||||
|     it('Rounds exp down when given a decimal', function() { | ||||
|       user.stats.exp = 10.999; | ||||
|       user.stats.lvl = 29; | ||||
|       var expDisplay = statCalc.expDisplay(user); | ||||
|  | ||||
|       expect(expDisplay).to.eql('10/640'); | ||||
|     }); | ||||
|   }); | ||||
|  | ||||
|   describe('goldDisplay', function() { | ||||
|     it('displays gold', function() { | ||||
|       var gold = 30; | ||||
|       var goldDisplay = statCalc.goldDisplay(gold); | ||||
|  | ||||
|       expect(goldDisplay).to.eql(30); | ||||
|     }); | ||||
|  | ||||
|     it('Rounds gold down when given a decimal', function() { | ||||
|       var gold = 30.999; | ||||
|       var goldDisplay = statCalc.goldDisplay(gold); | ||||
|  | ||||
|       expect(goldDisplay).to.eql(30); | ||||
|     }); | ||||
|   }); | ||||
|  | ||||
|   describe('hpDisplay', function() { | ||||
|     it('displays hp as "hp / totalHP"', function() { | ||||
|       var hp = 34; | ||||
|       var hpDisplay = statCalc.hpDisplay(hp); | ||||
|  | ||||
|       expect(hpDisplay).to.eql('34/50'); | ||||
|     }); | ||||
|  | ||||
|     it('Rounds hp up when given a decimal', function() { | ||||
|  | ||||
|       var hp = 34.4; | ||||
|       var hpDisplay = statCalc.hpDisplay(hp); | ||||
|  | ||||
|       expect(hpDisplay).to.eql('35/50'); | ||||
|     }); | ||||
|   }); | ||||
|  | ||||
|   describe('mountMasterProgress', function() { | ||||
|     it('counts drop mounts that user has', function() { | ||||
|       user.items.mounts = { | ||||
|         "Hedgehog-Desert" : true, | ||||
|         "Octopus-CottonCandyPink" : true, | ||||
|         "TigerCub-White" : true, | ||||
|         "Wolf-Golden" : true, | ||||
|         "Owl-CottonCandyBlue" : true, | ||||
|         "Mammoth-Base" : true, | ||||
|         "Bunny-Skeleton" : true, | ||||
|         "Tiger-Spooky": true | ||||
|       } | ||||
|  | ||||
|       var mountMasterDisplay = statCalc.mountMasterProgress(user.items.mounts); | ||||
|  | ||||
|       expect(mountMasterDisplay).to.eql('2/90'); | ||||
|     }); | ||||
|  | ||||
|     it('does not count drop mounts with a value of false', function() { | ||||
|       user.items.mounts = { | ||||
|         "Hedgehog-Desert" : true, | ||||
|         "Octopus-CottonCandyPink" : true, | ||||
|         "TigerCub-White" : false, | ||||
|         "Wolf-Golden" : false, | ||||
|         "Owl-CottonCandyBlue" : true, | ||||
|         "Mammoth-Base" : true, | ||||
|         "Bunny-Skeleton" : true, | ||||
|         "Tiger-Spooky": true | ||||
|       } | ||||
|  | ||||
|       var mountMasterDisplay = statCalc.mountMasterProgress(user.items.mounts); | ||||
|  | ||||
|       expect(mountMasterDisplay).to.eql('0/90'); | ||||
|     }); | ||||
|   }); | ||||
|  | ||||
|   describe('mpDisplay', function() { | ||||
|     it('displays mp as "mp / totalMP"', function() { | ||||
|       user.fns = {}; | ||||
|       user.fns.statsComputed = function () { return { maxMP: 100 } }; | ||||
|       user.stats.mp = 30; | ||||
|       var mpDisplay = statCalc.mpDisplay(user); | ||||
|  | ||||
|       expect(mpDisplay).to.eql('30/100'); | ||||
|     }); | ||||
|  | ||||
|     it('Rounds mp down when given a decimal', function() { | ||||
|       user.fns = {}; | ||||
|       user.fns.statsComputed = function () { return { maxMP: 100 } }; | ||||
|       user.stats.mp = 30.99; | ||||
|       var mpDisplay = statCalc.mpDisplay(user); | ||||
|  | ||||
|       expect(mpDisplay).to.eql('30/100'); | ||||
|     }); | ||||
|   }); | ||||
|  | ||||
|   describe('totalCount', function() { | ||||
|     it('counts all pets that user has', function() { | ||||
|       user.items.pets = { | ||||
|         "BearCub-Base" : 5, | ||||
|         "BearCub-CottonCandyBlue" : 5, | ||||
|         "Cactus-Zombie" : 5, | ||||
|         "Deer-Golden" : 5, | ||||
|         "Deer-Red" : 5, | ||||
|         "Egg-Desert" : 5, | ||||
|         "MantisShrimp-Base" : 5 | ||||
|       } | ||||
|  | ||||
|       var petsFound = statCalc.totalCount(user.items.pets); | ||||
|  | ||||
|       expect(petsFound).to.eql(7); | ||||
|     }); | ||||
|  | ||||
|     it('includes pets that have a value of 0', function() { | ||||
|       user.items.pets = { | ||||
|         "BearCub-Base" : 0, | ||||
|         "BearCub-CottonCandyBlue" : 5, | ||||
|         "Cactus-Zombie" : 0, | ||||
|         "Deer-Golden" : 0, | ||||
|         "Deer-Red" : 0, | ||||
|         "Egg-Desert" : 0, | ||||
|         "MantisShrimp-Base" : 5 | ||||
|       } | ||||
|  | ||||
|       var petsFound = statCalc.totalCount(user.items.pets); | ||||
|  | ||||
|       expect(petsFound).to.eql(7); | ||||
|     }); | ||||
|  | ||||
|     it('includes pets that have a value of -1', function() { | ||||
|       user.items.pets = { | ||||
|         "BearCub-Base" : -1, | ||||
|         "BearCub-CottonCandyBlue" : 5, | ||||
|         "Cactus-Zombie" : -1, | ||||
|         "Deer-Golden" : -1, | ||||
|         "Deer-Red" : -1, | ||||
|         "Egg-Desert" : -1, | ||||
|         "MantisShrimp-Base" : 5 | ||||
|       } | ||||
|  | ||||
|       var petsFound = statCalc.totalCount(user.items.pets); | ||||
|  | ||||
|       expect(petsFound).to.eql(7); | ||||
|     }); | ||||
|  | ||||
|     it('counts all mounts that user has', function() { | ||||
|       user.items.mounts = { | ||||
|         "Hedgehog-Desert" : true, | ||||
|         "Octopus-CottonCandyPink" : true, | ||||
|         "TigerCub-White" : true, | ||||
|         "Wolf-Golden" : true, | ||||
|         "Owl-CottonCandyBlue" : true, | ||||
|         "Mammoth-Base" : true, | ||||
|         "Bunny-Skeleton" : true | ||||
|       } | ||||
|  | ||||
|       var mountsFound = statCalc.totalCount(user.items.mounts); | ||||
|  | ||||
|       expect(mountsFound).to.eql(7); | ||||
|     }); | ||||
|  | ||||
|     it('inlcudes mounts with a value of false', function() { | ||||
|       user.items.mounts = { | ||||
|         "Hedgehog-Desert" : false, | ||||
|         "Octopus-CottonCandyPink" : true, | ||||
|         "TigerCub-White" : false, | ||||
|         "Wolf-Golden" : false, | ||||
|         "Owl-CottonCandyBlue" : false, | ||||
|         "Mammoth-Base" : true, | ||||
|         "Bunny-Skeleton" : false | ||||
|       } | ||||
|  | ||||
|       var mountsFound = statCalc.totalCount(user.items.mounts); | ||||
|  | ||||
|       expect(mountsFound).to.eql(7); | ||||
|     }); | ||||
|   }); | ||||
| }); | ||||
| @@ -1,52 +0,0 @@ | ||||
| 'use strict'; | ||||
|  | ||||
| describe('Tags Service', function() { | ||||
|   var rootScope, tags, user, $httpBackend; | ||||
|   var apiV3Prefix = 'api/v3/tags'; | ||||
|  | ||||
|   beforeEach(function() { | ||||
|     module(function($provide) { | ||||
|       user = specHelper.newUser(); | ||||
|       $provide.value('User', {user: user}); | ||||
|     }); | ||||
|  | ||||
|     inject(function(_$httpBackend_, _$rootScope_, Tags, User) { | ||||
|       $httpBackend = _$httpBackend_; | ||||
|       rootScope = _$rootScope_; | ||||
|       tags = Tags; | ||||
|     }); | ||||
|   }); | ||||
|  | ||||
|   it('calls get tags endpoint', function() { | ||||
|     $httpBackend.expectGET(apiV3Prefix).respond({}); | ||||
|     tags.getTags(); | ||||
|     $httpBackend.flush(); | ||||
|   }); | ||||
|  | ||||
|   it('calls post tags endpoint', function() { | ||||
|     $httpBackend.expectPOST(apiV3Prefix).respond({}); | ||||
|     tags.createTag(); | ||||
|     $httpBackend.flush(); | ||||
|   }); | ||||
|  | ||||
|   it('calls get tag endpoint', function() { | ||||
|     var tagId = 1; | ||||
|     $httpBackend.expectGET(apiV3Prefix + '/' + tagId).respond({}); | ||||
|     tags.getTag(tagId); | ||||
|     $httpBackend.flush(); | ||||
|   }); | ||||
|  | ||||
|   it('calls update tag endpoint', function() { | ||||
|     var tagId = 1; | ||||
|     $httpBackend.expectPUT(apiV3Prefix + '/' + tagId).respond({}); | ||||
|     tags.updateTag(tagId, {}); | ||||
|     $httpBackend.flush(); | ||||
|   }); | ||||
|  | ||||
|   it('calls delete tag endpoint', function() { | ||||
|     var tagId = 1; | ||||
|     $httpBackend.expectDELETE(apiV3Prefix + '/' + tagId).respond({}); | ||||
|     tags.deleteTag(tagId); | ||||
|     $httpBackend.flush(); | ||||
|   }); | ||||
| }); | ||||
| @@ -1,440 +0,0 @@ | ||||
| 'use strict'; | ||||
|  | ||||
| describe('Tasks Service', function() { | ||||
|   var rootScope, tasks, user, $httpBackend; | ||||
|   var apiV3Prefix = '/api/v3/tasks'; | ||||
|  | ||||
|   beforeEach(function() { | ||||
|     module(function($provide) { | ||||
|       user = specHelper.newUser(); | ||||
|       $provide.value('User', {user: user}); | ||||
|     }); | ||||
|  | ||||
|     inject(function(_$httpBackend_, _$rootScope_, Tasks, User) { | ||||
|       $httpBackend = _$httpBackend_; | ||||
|       rootScope = _$rootScope_; | ||||
|       rootScope.charts = {}; | ||||
|       tasks = Tasks; | ||||
|     }); | ||||
|  | ||||
|     rootScope.openModal = function() { | ||||
|       return { | ||||
|         result: { | ||||
|           then: function() {}, | ||||
|           catch: function() {}, | ||||
|         }, | ||||
|       }; | ||||
|     }; | ||||
|   }); | ||||
|  | ||||
|   it('calls get user tasks endpoint', function() { | ||||
|     $httpBackend.expectGET(apiV3Prefix + '/user').respond({}); | ||||
|     tasks.getUserTasks(); | ||||
|     $httpBackend.flush(); | ||||
|   }); | ||||
|  | ||||
|   it('calls post user tasks endpoint', function() { | ||||
|     $httpBackend.expectPOST(apiV3Prefix + '/user').respond({}); | ||||
|     tasks.createUserTasks(); | ||||
|     $httpBackend.flush(); | ||||
|   }); | ||||
|  | ||||
|   it('calls get challenge tasks endpoint', function() { | ||||
|     var challengeId = 1; | ||||
|     $httpBackend.expectGET(apiV3Prefix + '/challenge/' + challengeId).respond({}); | ||||
|     tasks.getChallengeTasks(challengeId); | ||||
|     $httpBackend.flush(); | ||||
|   }); | ||||
|  | ||||
|   it('calls create challenge tasks endpoint', function() { | ||||
|     var challengeId = 1; | ||||
|     $httpBackend.expectPOST(apiV3Prefix + '/challenge/' + challengeId).respond({}); | ||||
|     tasks.createChallengeTasks(challengeId, {}); | ||||
|     $httpBackend.flush(); | ||||
|   }); | ||||
|  | ||||
|   it('calls get task endpoint', function() { | ||||
|     var taskId = 1; | ||||
|     $httpBackend.expectGET(apiV3Prefix + '/' + taskId).respond({}); | ||||
|     tasks.getTask(taskId); | ||||
|     $httpBackend.flush(); | ||||
|   }); | ||||
|  | ||||
|   it('calls update task endpoint', function() { | ||||
|     var taskId = 1; | ||||
|     $httpBackend.expectPUT(apiV3Prefix + '/' + taskId).respond({}); | ||||
|     tasks.updateTask(taskId, {}); | ||||
|     $httpBackend.flush(); | ||||
|   }); | ||||
|  | ||||
|   it('calls delete task endpoint', function() { | ||||
|     var taskId = 1; | ||||
|     $httpBackend.expectDELETE(apiV3Prefix + '/' + taskId).respond({}); | ||||
|     tasks.deleteTask(taskId); | ||||
|     $httpBackend.flush(); | ||||
|   }); | ||||
|  | ||||
|   it('calls score task endpoint', function() { | ||||
|     var taskId = 1; | ||||
|     var direction = "down"; | ||||
|     $httpBackend.expectPOST(apiV3Prefix + '/' + taskId + '/score/' + direction).respond({}); | ||||
|     tasks.scoreTask(taskId, direction); | ||||
|     $httpBackend.flush(); | ||||
|   }); | ||||
|  | ||||
|   it('calls move task endpoint', function() { | ||||
|     var taskId = 1; | ||||
|     var position = 0; | ||||
|     $httpBackend.expectPOST(apiV3Prefix + '/' + taskId + '/move/to/' + position).respond({}); | ||||
|     tasks.moveTask(taskId, position); | ||||
|     $httpBackend.flush(); | ||||
|   }); | ||||
|  | ||||
|   it('calls group move task endpoint', function() { | ||||
|     var taskId = 1; | ||||
|     var position = 0; | ||||
|     $httpBackend.expectPOST('/api/v3/group-tasks/' + taskId + '/move/to/' + position).respond({}); | ||||
|     tasks.moveGroupTask(taskId, position); | ||||
|     $httpBackend.flush(); | ||||
|   }); | ||||
|  | ||||
|   it('calls add check list item endpoint', function() { | ||||
|     var taskId = 1; | ||||
|     $httpBackend.expectPOST(apiV3Prefix + '/' + taskId + '/checklist').respond({}); | ||||
|     tasks.addChecklistItem(taskId, {}); | ||||
|     $httpBackend.flush(); | ||||
|   }); | ||||
|  | ||||
|   it('calls score check list item endpoint', function() { | ||||
|     var taskId = 1; | ||||
|     var itemId = 2; | ||||
|     $httpBackend.expectPOST(apiV3Prefix + '/' + taskId + '/checklist/' + itemId + '/score').respond({}); | ||||
|     tasks.scoreCheckListItem(taskId, itemId); | ||||
|     $httpBackend.flush(); | ||||
|   }); | ||||
|  | ||||
|   it('calls update check list item endpoint', function() { | ||||
|     var taskId = 1; | ||||
|     var itemId = 2; | ||||
|     $httpBackend.expectPUT(apiV3Prefix + '/' + taskId + '/checklist/' + itemId).respond({}); | ||||
|     tasks.updateChecklistItem(taskId, itemId, {}); | ||||
|     $httpBackend.flush(); | ||||
|   }); | ||||
|  | ||||
|   it('calls remove check list item endpoint', function() { | ||||
|     var taskId = 1; | ||||
|     var itemId = 2; | ||||
|     $httpBackend.expectDELETE(apiV3Prefix + '/' + taskId + '/checklist/' + itemId).respond({}); | ||||
|     tasks.removeChecklistItem(taskId, itemId); | ||||
|     $httpBackend.flush(); | ||||
|   }); | ||||
|  | ||||
|   it('calls add tag to list item endpoint', function() { | ||||
|     var taskId = 1; | ||||
|     var tagId = 2; | ||||
|     $httpBackend.expectPOST(apiV3Prefix + '/' + taskId + '/tags/' + tagId).respond({}); | ||||
|     tasks.addTagToTask(taskId, tagId); | ||||
|     $httpBackend.flush(); | ||||
|   }); | ||||
|  | ||||
|   it('calls remove tag to list item endpoint', function() { | ||||
|     var taskId = 1; | ||||
|     var tagId = 2; | ||||
|     $httpBackend.expectDELETE(apiV3Prefix + '/' + taskId + '/tags/' + tagId).respond({}); | ||||
|     tasks.removeTagFromTask(taskId, tagId); | ||||
|     $httpBackend.flush(); | ||||
|   }); | ||||
|  | ||||
|   it('calls unlinkOneTask endpoint', function() { | ||||
|     var taskId = 1; | ||||
|     var keep = "keep"; | ||||
|     $httpBackend.expectPOST(apiV3Prefix + '/unlink-one/' + taskId + '?keep=' + keep).respond({}); | ||||
|     tasks.unlinkOneTask(taskId); | ||||
|     $httpBackend.flush(); | ||||
|   }); | ||||
|  | ||||
|   it('calls unlinkAllTasks endpoint', function() { | ||||
|     var challengeId = 1; | ||||
|     var keep = "keep-all"; | ||||
|     $httpBackend.expectPOST(apiV3Prefix + '/unlink-all/' + challengeId + '?keep=' + keep).respond({}); | ||||
|     tasks.unlinkAllTasks(challengeId); | ||||
|     $httpBackend.flush(); | ||||
|   }); | ||||
|  | ||||
|   it('calls clear completed todo task endpoint', function() { | ||||
|     $httpBackend.expectPOST(apiV3Prefix + '/clearCompletedTodos').respond({}); | ||||
|     tasks.clearCompletedTodos(); | ||||
|     $httpBackend.flush(); | ||||
|   }); | ||||
|  | ||||
|   describe('editTask', function() { | ||||
|     var task; | ||||
|  | ||||
|     beforeEach(function(){ | ||||
|       task = specHelper.newTask(); | ||||
|     }); | ||||
|  | ||||
|     it('sets _editing to true', function() { | ||||
|       tasks.editTask(task, user); | ||||
|       expect(task._editing).to.eql(true); | ||||
|     }); | ||||
|  | ||||
|     it('sets _tags to true by default', function() { | ||||
|       tasks.editTask(task, user); | ||||
|  | ||||
|       expect(task._tags).to.eql(true); | ||||
|     }); | ||||
|  | ||||
|     it('sets _tags to false if preference for collapsed tags is turned on', function() { | ||||
|       user.preferences.tagsCollapsed = true; | ||||
|       tasks.editTask(task, user); | ||||
|  | ||||
|       expect(task._tags).to.eql(false); | ||||
|     }); | ||||
|  | ||||
|     it('sets _advanced to true by default', function(){ | ||||
|       user.preferences.advancedCollapsed = true; | ||||
|       tasks.editTask(task, user); | ||||
|  | ||||
|       expect(task._advanced).to.eql(false); | ||||
|     }); | ||||
|  | ||||
|     it('sets _advanced to false if preference for collapsed advance menu is turned on', function() { | ||||
|       user.preferences.advancedCollapsed = false; | ||||
|       tasks.editTask(task, user); | ||||
|  | ||||
|       expect(task._advanced).to.eql(true); | ||||
|     }); | ||||
|  | ||||
|     it('closes task chart if it exists', function() { | ||||
|       rootScope.charts[task.id] = true; | ||||
|  | ||||
|       tasks.editTask(task, user); | ||||
|       expect(rootScope.charts[task.id]).to.eql(false); | ||||
|     }); | ||||
|   }); | ||||
|  | ||||
|   describe('cancelTaskEdit', function() { | ||||
|     var task; | ||||
|  | ||||
|     beforeEach(function(){ | ||||
|       task = specHelper.newTask(); | ||||
|     }); | ||||
|  | ||||
|     it('sets _editing to false', function() { | ||||
|       task._editing = true; | ||||
|       tasks.cancelTaskEdit(task); | ||||
|       expect(task._editing).to.eql(false); | ||||
|     }); | ||||
|   }); | ||||
|  | ||||
|   describe('cloneTask', function() { | ||||
|  | ||||
|     context('generic tasks', function() { | ||||
|       it('clones the data from a task', function() { | ||||
|         var task = specHelper.newTask(); | ||||
|         var clonedTask = tasks.cloneTask(task); | ||||
|  | ||||
|         expect(clonedTask.text).to.eql(task.text); | ||||
|         expect(clonedTask.notes).to.eql(task.notes); | ||||
|         expect(clonedTask.tags.includedTag).to.eql(task.tags.includedTag); | ||||
|         expect(clonedTask.tags.notIncludedTag).to.eql(task.tags.notIncludedTag); | ||||
|         expect(clonedTask.priority).to.eql(task.priority); | ||||
|         expect(clonedTask.attribute).to.eql(task.attribute); | ||||
|       }); | ||||
|  | ||||
|       it('does not clone original task\'s _id', function() { | ||||
|         var task = specHelper.newTask(); | ||||
|         var clonedTask = tasks.cloneTask(task); | ||||
|  | ||||
|         expect(clonedTask._id).to.exist; | ||||
|         expect(clonedTask._id).to.not.eql(task._id); | ||||
|       }); | ||||
|  | ||||
|       it('does not clone original task\'s dateCreated attribute', function() { | ||||
|         var task = specHelper.newTask({ | ||||
|           createdAt: new Date(2014, 5, 1, 1, 1, 1, 1), | ||||
|         }); | ||||
|         var clonedTask = tasks.cloneTask(task); | ||||
|  | ||||
|         expect(clonedTask.createdAt).to.exist; | ||||
|         expect(clonedTask.createdAt).to.not.eql(task.createdAt); | ||||
|       }); | ||||
|  | ||||
|       it('does not clone original task\'s value', function() { | ||||
|         var task = specHelper.newTask({ | ||||
|           value: 130 | ||||
|         }); | ||||
|         var clonedTask = tasks.cloneTask(task); | ||||
|  | ||||
|         expect(clonedTask.value).to.exist; | ||||
|         expect(clonedTask.value).to.not.eql(task.value); | ||||
|       }); | ||||
|     }); | ||||
|  | ||||
|     context('Habits', function() { | ||||
|  | ||||
|       it('clones a habit', function() { | ||||
|         var habit = specHelper.newHabit({ | ||||
|           up: true, | ||||
|           down: false | ||||
|         }); | ||||
|         var clonedHabit = tasks.cloneTask(habit); | ||||
|  | ||||
|         expect(clonedHabit.type).to.eql('habit'); | ||||
|         expect(clonedHabit.up).to.eql(habit.up); | ||||
|         expect(clonedHabit.down).to.eql(habit.down); | ||||
|       }); | ||||
|     }); | ||||
|  | ||||
|     context('Dailys', function() { | ||||
|  | ||||
|       it('clones a daily', function() { | ||||
|         var daily = specHelper.newDaily({ | ||||
|           frequency: 'daily', | ||||
|           everyX: 3, | ||||
|           startDate: new Date(2014, 5, 1, 1, 1, 1, 1), | ||||
|         }); | ||||
|  | ||||
|         var clonedDaily = tasks.cloneTask(daily); | ||||
|  | ||||
|         expect(clonedDaily.type).to.eql('daily'); | ||||
|         expect(clonedDaily.frequency).to.eql(daily.frequency); | ||||
|         expect(clonedDaily.everyX).to.eql(daily.everyX); | ||||
|         expect(clonedDaily.startDate).to.eql(daily.startDate); | ||||
|       }); | ||||
|  | ||||
|       it('does not clone streak', function() { | ||||
|         var daily = specHelper.newDaily({ | ||||
|           streak: 11 | ||||
|         }); | ||||
|  | ||||
|         var clonedDaily = tasks.cloneTask(daily); | ||||
|  | ||||
|         expect(clonedDaily.streak).to.eql(0); | ||||
|       }); | ||||
|     }); | ||||
|  | ||||
|     context('Todos', function() { | ||||
|  | ||||
|       it('clones a todo', function() { | ||||
|         var todo = specHelper.newTodo(); | ||||
|         var clonedTodo = tasks.cloneTask(todo); | ||||
|  | ||||
|         expect(clonedTodo.type).to.eql('todo'); | ||||
|       }); | ||||
|  | ||||
|       it('does not clone due date', function() { | ||||
|         var todo = specHelper.newTodo({ | ||||
|           date: '2015-06-20' | ||||
|         }); | ||||
|  | ||||
|         var clonedTodo = tasks.cloneTask(todo); | ||||
|  | ||||
|         expect(clonedTodo.date).to.not.exist; | ||||
|       }); | ||||
|  | ||||
|       it('does not clone date completed', function() { | ||||
|         var todo = specHelper.newTodo({ | ||||
|           dateCompleted: new Date() | ||||
|         }); | ||||
|  | ||||
|         var clonedTodo = tasks.cloneTask(todo); | ||||
|  | ||||
|         expect(clonedTodo.dateCompleted).to.not.exist; | ||||
|       }); | ||||
|     }); | ||||
|  | ||||
|     context('Rewards', function() { | ||||
|  | ||||
|       it('clones a reward', function() { | ||||
|         var reward = specHelper.newReward(); | ||||
|         var clonedReward = tasks.cloneTask(reward); | ||||
|  | ||||
|         expect(clonedReward.type).to.eql('reward'); | ||||
|       }); | ||||
|  | ||||
|       it('does clone a reward\'s vaue', function() { | ||||
|         var reward = specHelper.newReward({ | ||||
|           value: 20 | ||||
|         }); | ||||
|         var clonedReward = tasks.cloneTask(reward); | ||||
|  | ||||
|         expect(clonedReward.value).to.eql(reward.value); | ||||
|       }); | ||||
|     }); | ||||
|  | ||||
|     context('complete', function() { | ||||
|       it('does not clone completed status', function() { | ||||
|         var todo = specHelper.newTodo({ | ||||
|           completed: true | ||||
|         }); | ||||
|  | ||||
|         var clonedTodo = tasks.cloneTask(todo); | ||||
|  | ||||
|         expect(clonedTodo.completed).to.eql(false); | ||||
|       }); | ||||
|     }); | ||||
|  | ||||
|     context('history', function() { | ||||
|  | ||||
|       it('does not clone history', function() { | ||||
|         var habit = specHelper.newHabit({ | ||||
|           history: [ | ||||
|             { date: Date.now, value: 3.1 }, | ||||
|             { date: Date.now, value: 2.7 } | ||||
|           ] | ||||
|         }); | ||||
|         var clonedHabit = tasks.cloneTask(habit); | ||||
|  | ||||
|         expect(clonedHabit.history).to.be.an.array; | ||||
|         expect(clonedHabit.history).to.be.empty; | ||||
|       }); | ||||
|     }); | ||||
|  | ||||
|     context('checklists', function() { | ||||
|  | ||||
|       it('clones checklist text', function() { | ||||
|         var todo = specHelper.newTodo({ | ||||
|           checklist: [{ | ||||
|               completed: true, | ||||
|               text: 'checklist 1', | ||||
|               id: 'cl-1' | ||||
|             }, { | ||||
|               completed: false, | ||||
|               text: 'checklist 2', | ||||
|               id: 'cl-2' | ||||
|           }] | ||||
|         }); | ||||
|  | ||||
|         var clonedTodo = tasks.cloneTask(todo); | ||||
|  | ||||
|         expect(clonedTodo.checklist[0].text).to.eql(todo.checklist[0].text); | ||||
|         expect(clonedTodo.checklist[1].text).to.eql(todo.checklist[1].text); | ||||
|       }); | ||||
|  | ||||
|       it('does not clone complete or id attribute of checklist', function() { | ||||
|         var todo = specHelper.newTodo({ | ||||
|           checklist: [{ | ||||
|               completed: true, | ||||
|               text: 'checklist 1', | ||||
|               id: 'cl-1' | ||||
|             }, { | ||||
|               completed: false, | ||||
|               text: 'checklist 2', | ||||
|               id: 'cl-2' | ||||
|           }] | ||||
|         }); | ||||
|  | ||||
|         var clonedTodo = tasks.cloneTask(todo); | ||||
|  | ||||
|         expect(clonedTodo.checklist[0].completed).to.eql(false); | ||||
|         expect(clonedTodo.checklist[0].id).to.not.eql(todo.checklist[0].id); | ||||
|         expect(clonedTodo.checklist[0].id).to.exist; | ||||
|         expect(clonedTodo.checklist[1].completed).to.eql(false); | ||||
|         expect(clonedTodo.checklist[1].id).to.not.eql(todo.checklist[1].id); | ||||
|         expect(clonedTodo.checklist[1].id).to.exist; | ||||
|       }); | ||||
|     }); | ||||
|   }); | ||||
| }); | ||||
Some files were not shown because too many files have changed in this diff Show More
		Reference in New Issue
	
	Block a user