diff --git a/.gitignore b/.gitignore index 6fd10a4..e0f7577 100644 --- a/.gitignore +++ b/.gitignore @@ -1 +1,2 @@ -/node_modules/* \ No newline at end of file +/node_modules/* +/coverage/* diff --git a/gulpfile.js b/gulpfile.js index 7c36287..33fa4bc 100644 --- a/gulpfile.js +++ b/gulpfile.js @@ -5,7 +5,7 @@ var historyApiFallback = require('connect-history-api-fallback'); gulp.task('browser-sync', function(){ browserSync.init({ server: { - baseDir: "./", + baseDir: "./src", middleware: [historyApiFallback()] } }); diff --git a/karma.conf.js b/karma.conf.js new file mode 100644 index 0000000..3ebb56a --- /dev/null +++ b/karma.conf.js @@ -0,0 +1,85 @@ +// Karma configuration +// Generated on Mon Jan 04 2016 21:39:34 GMT+0300 (RTZ 2 (зима)) + +module.exports = function(config) { + config.set({ + + // base path that will be used to resolve all patterns (eg. files, exclude) + basePath: '', + + + // frameworks to use + // available frameworks: https://npmjs.org/browse/keyword/karma-adapter + frameworks: ['jasmine'], + + + // list of files / patterns to load in the browser + files: [ + 'node_modules/angular/angular.js', + 'node_modules/angular-route/angular-route.js', + 'node_modules/angular-resource/angular-resource.js', + 'node_modules/angular-mocks/angular-mocks.js', + 'src/app.js', + 'src/*.js', + 'src/*.html', + 'test/*.js' + ], + + + // list of files to exclude + exclude: [ + ], + + + // preprocess matching files before serving them to the browser + // available preprocessors: https://npmjs.org/browse/keyword/karma-preprocessor + preprocessors: { + 'src/*.html': ['ng-html2js'], + 'src/*.js': ['coverage'] + }, + + ngHtml2JsPreprocessor: { + moduleName: 'templates' + }, + + // test results reporter to use + // possible values: 'dots', 'progress' + // available reporters: https://npmjs.org/browse/keyword/karma-reporter + reporters: ['progress', 'coverage'], + + coverageReporter: { + type: 'html', + dir: 'coverage/' + }, + + // web server port + port: 9876, + + + // enable / disable colors in the output (reporters and logs) + colors: true, + + + // level of logging + // possible values: config.LOG_DISABLE || config.LOG_ERROR || config.LOG_WARN || config.LOG_INFO || config.LOG_DEBUG + logLevel: config.LOG_INFO, + + + // enable / disable watching file and executing tests whenever any file changes + autoWatch: false, + + + // start these browsers + // available browser launchers: https://npmjs.org/browse/keyword/karma-launcher + browsers: ['Chrome'], + + + // Continuous Integration mode + // if true, Karma captures browsers, runs the tests and exits + singleRun: true, + + // Concurrency level + // how many browser should be started simultaneous + concurrency: Infinity + }) +} diff --git a/package.json b/package.json new file mode 100644 index 0000000..39dae15 --- /dev/null +++ b/package.json @@ -0,0 +1,25 @@ +{ + "name": "mounts-test", + "version": "1.0.0", + "description": "Unit testing thSample application.", + "dependencies": { + "angular": "^1.3.15" + }, + "devDependencies": { + "angular-mocks": "~1.4.7", + "karma": "~0.13.10", + "jasmine-core": "~2.3.4", + "karma-jasmine": "~0.3.6", + "browser-sync": "~2.9.10", + "gulp": "~3.9.0", + "connect-history-api-fallback": "~1.1.0", + "karma-chrome-launcher": "~0.2.1", + "karma-ng-html2js-preprocessor": "~0.2.0", + "karma-coverage": "~0.5.2", + "angular-route": "~1.4.7", + "angular-resource": "~1.4.7" + }, + "scripts": { + "test": "karma start" + } +} \ No newline at end of file diff --git a/admin_countries_controller.js b/src/admin_countries_controller.js similarity index 100% rename from admin_countries_controller.js rename to src/admin_countries_controller.js diff --git a/admin_list.html b/src/admin_list.html similarity index 100% rename from admin_list.html rename to src/admin_list.html diff --git a/admin_places_controller.js b/src/admin_places_controller.js similarity index 100% rename from admin_places_controller.js rename to src/admin_places_controller.js diff --git a/admin_tour_controller.js b/src/admin_tour_controller.js similarity index 100% rename from admin_tour_controller.js rename to src/admin_tour_controller.js diff --git a/admin_tours_controller.js b/src/admin_tours_controller.js similarity index 100% rename from admin_tours_controller.js rename to src/admin_tours_controller.js diff --git a/app.js b/src/app.js similarity index 90% rename from app.js rename to src/app.js index 2ac74f5..87cdf9e 100644 --- a/app.js +++ b/src/app.js @@ -4,12 +4,7 @@ angular.module('Travel', ['ngRoute', 'ngResource']) .when('/',{ templateUrl: 'admin_list.html', controller: 'AdminToursController', - publicAccess: true, - resolve: { - currentUser: function(){ - return {name: 'Alex'}; - } - } + publicAccess: true }) .when('/admin/tours/:id',{ templateUrl: 'item.html', diff --git a/countries.html b/src/countries.html similarity index 100% rename from countries.html rename to src/countries.html diff --git a/index.html b/src/index.html similarity index 100% rename from index.html rename to src/index.html diff --git a/item.html b/src/item.html similarity index 100% rename from item.html rename to src/item.html diff --git a/list.html b/src/list.html similarity index 100% rename from list.html rename to src/list.html diff --git a/places.html b/src/places.html similarity index 100% rename from places.html rename to src/places.html diff --git a/tour_controller.js b/src/tour_controller.js similarity index 69% rename from tour_controller.js rename to src/tour_controller.js index 19c1e7c..4087e9c 100644 --- a/tour_controller.js +++ b/src/tour_controller.js @@ -1,6 +1,5 @@ angular.module('Travel').controller('TourController', function($scope, $routeParams) { angular.forEach(allTours, function(tour){ - if ($routeParams.slug == tour.slug) - $scope.tour = tour; + if ($routeParams.slug == tour.slug) $scope.tour = tour; }); }); \ No newline at end of file diff --git a/tours_controller.js b/src/tours_controller.js similarity index 100% rename from tours_controller.js rename to src/tours_controller.js diff --git a/test/admin_tours_controller_spec.js b/test/admin_tours_controller_spec.js new file mode 100644 index 0000000..b17a86a --- /dev/null +++ b/test/admin_tours_controller_spec.js @@ -0,0 +1,150 @@ +describe('AdminToursController', function() { + beforeEach(module('Travel')); + var $scope = {}; + var $httpBackend = null; + var resoures = ['Tour', 'Country', 'Place', 'Hotel']; + var tourForUpdate = null; + var tourApiUrl = 'https://api.parse.com/1/classes/Tour'; + var country = { name: 'Country name', objectId: '1231erw' }; + var hotel = { name: 'Hotel name', stars: 5 }; + var place = { name: 'Place name'}; + + beforeEach(inject(function($controller, _$httpBackend_){ + $controller('AdminToursController', {$scope: $scope}); + $httpBackend = _$httpBackend_; + resoures.forEach(function(i){ + $httpBackend.whenGET('https://api.parse.com/1/classes/' + i).respond(200, JSON.stringify({results: []})); + }); + })); + + describe('initialize controller', function() { + it('sets title to Путешествия', function() { + expect($scope.title).toBe('Путешествия'); + }); + + it('expects to call parse.com for tours list', function(){ + $httpBackend.expectGET('https://api.parse.com/1/classes/Tour').respond(200); + expect($httpBackend.verifyNoOutstandingExpectation).not.toThrow(); + }); + }); + + describe('CRUD', function() { + describe('update', function() { + beforeEach(function(){ + $scope.tours = [{ title: 'Old name', length: 10, objectId: '21re43f' }]; + tourForUpdate = $scope.tours[0]; + }); + + it('hides tour form', function(){ + spyOn($scope, 'hideEditForm'); + $scope.update(tourForUpdate); + expect($scope.hideEditForm).toHaveBeenCalledWith(tourForUpdate); + }); + + it('updates the tour values', function(){ + tourForUpdate.title = 'New name'; + $scope.update(tourForUpdate); + expect(tourForUpdate.title).toEqual('New name'); + }); + + it('makes request to parse.com', function(){ + $scope.update(tourForUpdate); + $httpBackend.expectPUT('https://api.parse.com/1/classes/Tour/' + tourForUpdate.objectId).respond(200); + expect($httpBackend.verifyNoOutstandingExpectation).not.toThrow(); + }); + }); + + describe('create', function() { + var newTour = null; + + beforeEach(function(){ + var newTour = { title: 'New tour name', length: 10, placeId: place, countryId: country, + hotel: hotel } + $scope.newTour = newTour; + var jsonResponse = JSON.stringify({results: [newTour]}); + $httpBackend.whenPOST(tourApiUrl).respond(jsonResponse); + }); + + it('makes request to api for create', function(){ + $httpBackend.expectPOST(tourApiUrl); + $scope.addTour(); + expect($httpBackend.verifyNoOutstandingExpectation).not.toThrow(); + }); + + it('adds new tour to scope', function(){ + expect($scope.tours.length).toEqual(0) + $scope.addTour(); + $httpBackend.flush(); + expect($scope.tours.length).toEqual(1); + expect($scope.tours[0].title).toEqual('New tour name'); + expect($scope.tours[0].length).toEqual(10); + }); + }); + + describe('delete', function(){ + var tourForDelete = null; + var tourForDeleteUrl = null; + + beforeEach(function(){ + $scope.tours = [ { title: 'Tour for delete', length: 10, objectId: 'ewrwr3e' }]; + tourForDelete = $scope.tours[0]; + tourForDeleteUrl = tourApiUrl + '/' + tourForDelete.objectId; + $httpBackend.whenGET(tourForDeleteUrl).respond(JSON.stringify({results: [tourForDelete]})); + $httpBackend.whenDELETE(tourForDeleteUrl).respond(200); + }); + + it('calls api for tour deletion', function(){ + $httpBackend.expectGET(tourForDeleteUrl); + $httpBackend.expectDELETE(tourForDeleteUrl); + $scope.delete(tourForDelete); + expect($httpBackend.verifyNoOutstandingExpectation).not.toThrow(); + }); + + it('delete tour from scope', function(){ + expect($scope.tours.length).toEqual(1); + $scope.delete(tourForDelete); + $httpBackend.flush(); + expect($scope.tours.length).toEqual(0); + }); + }); + }); + + describe('$scope.tourCountry', function(){ + beforeEach(function(){ + $scope.tourCountries = [country]; + $scope.tours = [ { title: 'Tour with Country', countryId: country }, + { title: 'Tour without Country'} ]; + }); + + it('returns tour country', function(){ + expect($scope.tourCountry($scope.tours[0])).toEqual(country.name); + }); + + it('returns blank string for tour without country', function(){ + expect($scope.tourCountry($scope.tours[1])).toEqual(''); + }); + }); + + describe('$scope.cancelEdit', function(){ + var tour = null; + + beforeEach(function(){ + $scope.tours = [ { title: 'Tour name', length: 10, objectId: 'ewrwr3e' }]; + tour = $scope.tours[0]; + $httpBackend.whenGET(tourApiUrl + '/' + tour.objectId).respond(JSON.stringify({results: [tour]})); + }); + + it('hides tour form', function(){ + spyOn($scope, 'hideEditForm'); + $scope.cancelEdit(tour); + expect($scope.hideEditForm).toHaveBeenCalledWith(tour); + }); + + it('resets tour values', function(){ + tour.title = 'New title'; + $scope.cancelEdit(tour); + $httpBackend.flush(); + expect($scope.tours[0].results[0].title).toEqual('Tour name'); + }) + }); +});