diff --git a/.gitignore b/.gitignore index 1cb23ccadb..7ec0d27fdb 100644 --- a/.gitignore +++ b/.gitignore @@ -23,3 +23,5 @@ test/*.map website/public/docs *.sublime-workspace coverage.html + +test/spec/translations.js diff --git a/Gruntfile.js b/Gruntfile.js index 2fe125c003..1712bfaf9f 100644 --- a/Gruntfile.js +++ b/Gruntfile.js @@ -154,8 +154,8 @@ module.exports = function(grunt) { fileNameFormat: '${name}-${hash}.${ext}' }, src: [ - 'website/build/*.js', - 'website/build/*.css', + 'website/build/*.js', + 'website/build/*.css', 'website/build/favicon.ico', 'website/build/common/dist/sprites/*.png', 'website/build/common/img/sprites/backer-only/*.gif', @@ -166,7 +166,7 @@ module.exports = function(grunt) { } }, - nodemon: { + nodemon: { dev: { script: '<%= pkg.main %>' } @@ -202,7 +202,7 @@ module.exports = function(grunt) { _.each(files[key].js, function(val){ var path = "./"; - if( val.indexOf('common/') == -1) + if( val.indexOf('common/') == -1) path = './website/public/'; js.push(path + val); }); @@ -213,7 +213,7 @@ module.exports = function(grunt) { var path = "./"; if( val.indexOf('common/') == -1) { path = (val == 'app.css' || val == 'static.css') ? './website/build/' : './website/public/'; - } + } css.push(path + val) }); @@ -231,9 +231,19 @@ module.exports = function(grunt) { grunt.registerTask('compile:sprites', ['clean:sprite', 'sprite', 'cssmin']); grunt.registerTask('build:prod', ['loadManifestFiles', 'clean:build', 'browserify', 'uglify', 'stylus', 'cssmin', 'copy:build', 'hashres']); grunt.registerTask('build:dev', ['browserify', 'stylus']); + grunt.registerTask('build:test', ['test:prepare:translations', 'build:dev']); grunt.registerTask('run:dev', [ 'build:dev', 'concurrent' ]); + grunt.registerTask('test:prepare:translations', function() { + require('coffee-script'); + var i18n = require('./website/src/i18n'), + fs = require('fs'); + fs.writeFileSync('test/spec/translations.js', + "if(!window.env) window.env = {};\n" + + "window.env.translations = " + JSON.stringify(i18n.translations['en']) + ';'); + }); + if(process.env.NODE_ENV == 'production') grunt.registerTask('default', ['build:prod']); else diff --git a/common/dist/scripts/habitrpg-shared.js b/common/dist/scripts/habitrpg-shared.js index 16646bad9e..e965e50231 100644 --- a/common/dist/scripts/habitrpg-shared.js +++ b/common/dist/scripts/habitrpg-shared.js @@ -4889,7 +4889,11 @@ module.exports = { if ((locale == null) || (!module.exports.strings && !module.exports.translations[locale])) { locale = 'en'; } - string = !module.exports.strings ? module.exports.translations[locale][stringName] : module.exports.strings[stringName]; + if (module.exports.strings) { + string = module.exports.strings[stringName]; + } else { + string = module.exports.translations[locale] && module.exports.translations[locale][stringName]; + } clonedVars = _.clone(vars) || {}; clonedVars.locale = locale; if (string) { @@ -4900,7 +4904,11 @@ module.exports = { return 'Error processing string. Please report to http://github.com/HabitRPG/habitrpg.'; } } else { - stringNotFound = !module.exports.strings ? module.exports.translations[locale].stringNotFound : module.exports.strings.stringNotFound; + if (module.exports.strings) { + stringNotFound = module.exports.strings.stringNotFound; + } else if (module.exports.translations[locale]) { + stringNotFound = module.exports.translations[locale] && module.exports.translations[locale].stringNotFound; + } try { return _.template(stringNotFound, { string: stringName diff --git a/common/script/i18n.coffee b/common/script/i18n.coffee index 8b99175f5f..efd19c911a 100644 --- a/common/script/i18n.coffee +++ b/common/script/i18n.coffee @@ -1,6 +1,6 @@ _ = require 'lodash' -module.exports = +module.exports = strings: null, # Strings for one single language translations: {} # Strings for multiple languages {en: strings, de: strings, ...} t: (stringName) -> # Other parameters allowed are vars (Object) and locale (String) @@ -14,10 +14,16 @@ module.exports = locale = arguments[2] locale = 'en' if (!locale? or (!module.exports.strings and !module.exports.translations[locale])) - string = if (!module.exports.strings) then module.exports.translations[locale][stringName] else module.exports.strings[stringName] - - clonedVars = _.clone(vars) or {}; - clonedVars.locale = locale; + + if module.exports.strings + string = module.exports.strings[stringName] + else + string = + module.exports.translations[locale] and + module.exports.translations[locale][stringName] + + clonedVars = _.clone(vars) or {} + clonedVars.locale = locale if string try @@ -25,8 +31,14 @@ module.exports = catch e 'Error processing string. Please report to http://github.com/HabitRPG/habitrpg.' else - stringNotFound = if (!module.exports.strings) then module.exports.translations[locale].stringNotFound else module.exports.strings.stringNotFound + if module.exports.strings + stringNotFound = module.exports.strings.stringNotFound + else if module.exports.translations[locale] + stringNotFound = + module.exports.translations[locale] and + module.exports.translations[locale].stringNotFound + try _.template(stringNotFound, {string: stringName}) catch e - 'Error processing string. Please report to http://github.com/HabitRPG/habitrpg.' \ No newline at end of file + 'Error processing string. Please report to http://github.com/HabitRPG/habitrpg.' diff --git a/karma.conf.js b/karma.conf.js index 83c9e0a776..53a7e4999b 100644 --- a/karma.conf.js +++ b/karma.conf.js @@ -34,6 +34,8 @@ module.exports = function(config) { 'website/public/bower_components/js-emoji/emoji.js', 'common/dist/scripts/habitrpg-shared.js', + "test/spec/translations.js", + "website/public/js/env.js", "website/public/js/app.js", @@ -96,6 +98,8 @@ module.exports = function(config) { // - IE (only Windows) browsers: ['PhantomJS'], + // Enable mocha-style reporting, for better test visibility + reporters: ['mocha'], // Continuous Integration mode // if true, it capture browsers, run tests and exit diff --git a/package.json b/package.json index 5f9a9d42b4..64c576a05d 100644 --- a/package.json +++ b/package.json @@ -92,6 +92,7 @@ "karma-html2js-preprocessor": "~0.1.0", "karma-jasmine": "~0.1.3", "karma-mocha": "0.1.3", + "karma-mocha-reporter": "^1.0.2", "karma-ng-html2js-preprocessor": "~0.1.0", "karma-phantomjs-launcher": "~0.1.0", "karma-requirejs": "~0.2.0", diff --git a/protractor.conf.js b/protractor.conf.js index 32999a2233..fef43baab8 100644 --- a/protractor.conf.js +++ b/protractor.conf.js @@ -19,6 +19,6 @@ exports.config = { // Options to be passed to Jasmine-node. jasmineNodeOpts: { showColors: true, - defaultTimeoutInterval: 30000 + defaultTimeoutInterval: 60000 } }; diff --git a/test/e2e/e2e.js b/test/e2e/e2e.js index 8bbdbe8a62..0f471df3d9 100644 --- a/test/e2e/e2e.js +++ b/test/e2e/e2e.js @@ -37,7 +37,7 @@ describe('front page', function() { var login = element(by.css("#login-tab input[value='Login']")); login.click(); var alertDialog = browser.switchTo().alert(); - expect(alertDialog.getText()).toMatch(/Username 'username' not found/); + expect(alertDialog.getText()).toMatch(/Username or password incorrect./); alertDialog.accept(); }); @@ -58,4 +58,4 @@ describe('front page', function() { expect(url).not.toMatch(/static\/front/); }); }); -}); \ No newline at end of file +}); diff --git a/test/run_tests.sh b/test/run_tests.sh index 43b395c833..2d14f46ebe 100755 --- a/test/run_tests.sh +++ b/test/run_tests.sh @@ -5,39 +5,55 @@ TEST_DB_URI="mongodb://localhost/$TEST_DB" TEST_SERVER_PORT=3001 # Build assets -grunt build:dev +grunt build:test # Launch Node server and Selenium -echo "Recreating test database" +echo "= Recreating test database" mongo "$TEST_DB" --eval "db.dropDatabase()" if [ -z "$TRAVIS" ]; then - ./node_modules/protractor/bin/webdriver-manager update - ./node_modules/protractor/bin/webdriver-manager start > /dev/null & - trap "curl http://localhost:4444/selenium-server/driver/?cmd=shutDownSeleniumServer" EXIT + if [ -z "$1" ] || [ "$1" == "protractor" ]; then + ./node_modules/protractor/bin/webdriver-manager update + ./node_modules/protractor/bin/webdriver-manager start > /dev/null & + trap "curl http://localhost:4444/selenium-server/driver/?cmd=shutDownSeleniumServer" EXIT - # Wait for selenium - MAX_WAIT=30 - WAITED=0 - until nc -z localhost 4444; do - if [ $WAITED -ge $MAX_WAIT ]; then - echo "Waited $MAX_WAIT seconds, but Selenium never responded" >&2 - kill $NODE_PID - exit 1 - fi - sleep 1 - let 'WAITED+=1' - done + # Wait for selenium + MAX_WAIT=30 + WAITED=0 + until nc -z localhost 4444; do + if [ $WAITED -ge $MAX_WAIT ]; then + echo "Waited $MAX_WAIT seconds, but Selenium never responded" >&2 + kill $NODE_PID + exit 1 + fi + sleep 1 + let 'WAITED+=1' + done + fi fi NODE_DB_URI="$TEST_DB_URI" PORT=$TEST_SERVER_PORT node ./website/src/server.js > /dev/null & NODE_PID=$! trap "kill $NODE_PID" EXIT -NODE_ENV=testing mocha || exit $? - -if [ -z "$TRAVIS" ]; then - NODE_ENV=testing ./node_modules/protractor/bin/protractor protractor.conf.js || exit $? +if [ -z "$1" ] || [ "$1" == "mocha" ]; then + echo "= Running mocha specs" + NODE_ENV=testing mocha || exit $? fi -NODE_ENV=testing grunt karma:continuous +# If we're only running protractor, we need to let the server spin up. +if [ "$1" == "protractor" ]; then + sleep 2 +fi + +if [ -z "$TRAVIS" ]; then + if [ -z "$1" ] || [ "$1" == "protractor" ]; then + echo "= Running protractor specs" + NODE_ENV=testing ./node_modules/protractor/bin/protractor protractor.conf.js || exit $? + fi +fi + +if [ -z "$1" ] || [ "$1" == "karma" ]; then + echo "= Running karma specs" + NODE_ENV=testing grunt karma:continuous || exit $? +fi diff --git a/test/spec/authCtrlSpec.js b/test/spec/authCtrlSpec.js index 24648226ed..598fe0dee4 100644 --- a/test/spec/authCtrlSpec.js +++ b/test/spec/authCtrlSpec.js @@ -1,8 +1,6 @@ 'use strict'; -// @TODO translations aren't loading - -xdescribe('Auth Controller', function() { +describe('Auth Controller', function() { describe('AuthCtrl', function(){ var scope, ctrl, user, $httpBackend, $window; diff --git a/test/spec/filtersCtrlSpec.js b/test/spec/filtersCtrlSpec.js index ae96ec565c..41d53a4cb3 100644 --- a/test/spec/filtersCtrlSpec.js +++ b/test/spec/filtersCtrlSpec.js @@ -3,7 +3,6 @@ describe('Filters Controller', function() { var scope, user; - beforeEach(module('habitrpg')); beforeEach(inject(function($rootScope, $controller, Shared) { user = specHelper.newUser(); Shared.wrap(user); diff --git a/test/spec/groupServicesSpec.js b/test/spec/groupServicesSpec.js index 6f6dfe0c49..9d38dd7372 100644 --- a/test/spec/groupServicesSpec.js +++ b/test/spec/groupServicesSpec.js @@ -1,15 +1,18 @@ 'use strict'; -// @TODO the requests via $resource seem to be -// doing a full page reload which breaks the specs +describe('groupServices', function() { + var $httpBackend, $http, groups; -xdescribe('groupServices', function() { - var $httpBackend, groups; + beforeEach(function() { + module(function($provide) { + $provide.value('User', {}); + }); - beforeEach(inject(function(_$httpBackend_, Groups) { + inject(function(_$httpBackend_, Groups, User) { $httpBackend = _$httpBackend_; groups = Groups; - })); + }); + }); it('calls party endpoint', function() { $httpBackend.expectGET('/api/v2/groups/party').respond({}); diff --git a/test/spec/inventoryCtrlSpec.js b/test/spec/inventoryCtrlSpec.js index cce04cd9b6..65e36911fc 100644 --- a/test/spec/inventoryCtrlSpec.js +++ b/test/spec/inventoryCtrlSpec.js @@ -1,23 +1,31 @@ 'use strict'; -// @TODO Address why translations aren't loading -// Possibly related to https://github.com/HabitRPG/habitrpg/commit/5aa401524934e6d9071f13cb2ccca0dba13cdcff describe('Inventory Controller', function() { var scope, ctrl, user, $rootScope; - beforeEach(inject(function($rootScope, $controller, Shared){ - user = specHelper.newUser(); - user.balance = 4, - user.items = {eggs: {Cactus: 1}, hatchingPotions: {Base: 1}, food: {Meat: 1}, pets: {}}; - Shared.wrap(user); - var mockWindow = { - confirm: function(msg){ - return true; - } - }; - scope = $rootScope.$new(); - ctrl = $controller('InventoryCtrl', {$scope: scope, User: {user: user}, $window: mockWindow}); - })); + beforeEach(function() { + module(function($provide) { + $provide.value('User', {}); + }); + + inject(function($rootScope, $controller, Shared){ + user = specHelper.newUser(); + user.balance = 4, + user.items = {eggs: {Cactus: 1}, hatchingPotions: {Base: 1}, food: {Meat: 1}, pets: {}, mounts: {}}; + Shared.wrap(user); + var mockWindow = { + confirm: function(msg){ + return true; + } + }; + scope = $rootScope.$new(); + + // Load RootCtrl to ensure shared behaviors are loaded + $controller('RootCtrl', {$scope: scope, User: {user: user}, $window: mockWindow}); + + ctrl = $controller('InventoryCtrl', {$scope: scope, User: {user: user}, $window: mockWindow}); + }); + }); it('starts without any item selected', function(){ expect(scope.selectedEgg).to.eql(null); @@ -35,7 +43,7 @@ describe('Inventory Controller', function() { expect(scope.selectedPotion.key).to.eql('Base'); }); - xit('hatches a pet', function(){ + it('hatches a pet', function(){ scope.chooseEgg('Cactus'); scope.choosePotion('Base'); expect(user.items.eggs).to.eql({Cactus: 0}); @@ -66,13 +74,13 @@ describe('Inventory Controller', function() { expect(user.stats.gp).to.eql(1); }); - xit('chooses a pet', function(){ + it('chooses a pet', function(){ user.items.pets['Cactus-Base'] = 5; scope.choosePet('Cactus', 'Base'); expect(user.items.currentPet).to.eql('Cactus-Base'); }); - xit('purchases an egg', inject(function(Content){ + 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}) diff --git a/test/spec/rootCtrlSpec.js b/test/spec/rootCtrlSpec.js index 8206a327d1..5f4b61b1d2 100644 --- a/test/spec/rootCtrlSpec.js +++ b/test/spec/rootCtrlSpec.js @@ -1,18 +1,24 @@ 'use strict'; -xdescribe('Root Controller', function() { +// @TODO: Something here is calling a full page reload +describe('Root Controller', function() { var scope, user, ctrl; - beforeEach(inject(function($rootScope, $controller) { - scope = $rootScope.$new(); - scope.loginUsername = 'user' - scope.loginPassword = 'pass' - user = specHelper.newUser(); + beforeEach(function () { + module(function($provide) { + $provide.value('User', {}); + }); + + inject(function($rootScope, $controller) { + scope = $rootScope.$new(); + scope.loginUsername = 'user' + scope.loginPassword = 'pass' + user = specHelper.newUser(); + + ctrl = $controller('RootCtrl', {$scope: scope, User: {user: user}}); + }); + }); - ctrl = $controller('RootCtrl', {$scope: scope, User: {user: user}}); - })); - - // @TODO: Fix translations not loading here it('shows contributor level text', function(){ expect(scope.contribText()).to.eql(undefined); expect(scope.contribText(null, {npc: 'NPC'})).to.eql('NPC'); @@ -24,8 +30,9 @@ xdescribe('Root Controller', function() { 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('Heroic Blacksmith'); - expect(scope.contribText({level: 8, text: 'Blacksmith'}, {npc: 'NPC'})).to.eql('NPC'); + 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'); }); });