html:
<!doctype html>
<html ng-app="app">
<head>
<meta charset="utf-8">
<title>AngularJS dev</title>
<link href="bootstrap/css/bootstrap.css" rel="stylesheet">
<link href="css/style.css" rel="stylesheet">
<script src="js/vendor/angular.js"></script>
<script src="js/app.js"></script>
<script src="js/appCtrl.js"></script>
<script src="js/appDrct.js"></script>
<script src="js/appSrvc.js"></script>
<script src="js/appFltr.js"></script>
</head>
<body>
<article ng-controller="appCtrl" class="container">
<button ng-click="incrementCount()">Increment</button>
count: {{count}}
<div class-well>{{2 + 2}}</div>
<h3>List of countries</h3>
<ul>
<li ng-repeat="country in srvc.countriesList">
{{country.name}} - {{country.capital | reverse}}
</li>
</ul>
<h3>Check if country is in the list</h3>
<p>Country Name:
<input type="text" ng-model="country.name" />
</p>
<p>
<button ng-click="checkCountry()">Check country</button>
</p>
</article>
</body>
</html> |
<!doctype html>
<html ng-app="app">
<head>
<meta charset="utf-8">
<title>AngularJS dev</title>
<link href="bootstrap/css/bootstrap.css" rel="stylesheet">
<link href="css/style.css" rel="stylesheet">
<script src="js/vendor/angular.js"></script>
<script src="js/app.js"></script>
<script src="js/appCtrl.js"></script>
<script src="js/appDrct.js"></script>
<script src="js/appSrvc.js"></script>
<script src="js/appFltr.js"></script>
</head>
<body>
<article ng-controller="appCtrl" class="container">
<button ng-click="incrementCount()">Increment</button>
count: {{count}}
<div class-well>{{2 + 2}}</div>
<h3>List of countries</h3>
<ul>
<li ng-repeat="country in srvc.countriesList">
{{country.name}} - {{country.capital | reverse}}
</li>
</ul>
<h3>Check if country is in the list</h3>
<p>Country Name:
<input type="text" ng-model="country.name" />
</p>
<p>
<button ng-click="checkCountry()">Check country</button>
</p>
</article>
</body>
</html>
AngularJS unit testing using Karma and Jasmine
AngularJS controller unit testing:
appCtrl.js:
app.controller('appCtrl', function ($scope, appSrvc) {
$scope.count = 5;
$scope.srvc = appSrvc; // bind scope variable to service
$scope.incrementCount = function() {
$scope.count = $scope.count + 1;
};
$scope.checkCountry = function() {
if ($scope.srvc.checkCountry($scope.country.name)) {
alert($scope.country.name + ' is in the list');
} else {
alert($scope.country.name + ' is not in the list');
}
};
}); |
app.controller('appCtrl', function ($scope, appSrvc) {
$scope.count = 5;
$scope.srvc = appSrvc; // bind scope variable to service
$scope.incrementCount = function() {
$scope.count = $scope.count + 1;
};
$scope.checkCountry = function() {
if ($scope.srvc.checkCountry($scope.country.name)) {
alert($scope.country.name + ' is in the list');
} else {
alert($scope.country.name + ' is not in the list');
}
};
});
appCtrl.spec.js:
describe('appCtrl', function(){
var appCtrl, $scope;
beforeEach(function() { // executed before each 'it' is run
module('app'); // load the module
inject(function($controller, $rootScope) { // inject controller for testing
$scope = $rootScope.$new();
appCtrl = $controller('appCtrl', {
$scope: $scope
});
})
});
it('should have appCtrl controller toBeDefined', function() {
expect(appCtrl).toBeDefined();
});
it('should init counter value', function() {
expect($scope.count).toBeDefined();
expect($scope.count).toBe(5);
});
it('should change counter value', function() {
$scope.incrementCount();
expect($scope.count).toBe(6);
});
}); |
describe('appCtrl', function(){
var appCtrl, $scope;
beforeEach(function() { // executed before each 'it' is run
module('app'); // load the module
inject(function($controller, $rootScope) { // inject controller for testing
$scope = $rootScope.$new();
appCtrl = $controller('appCtrl', {
$scope: $scope
});
})
});
it('should have appCtrl controller toBeDefined', function() {
expect(appCtrl).toBeDefined();
});
it('should init counter value', function() {
expect($scope.count).toBeDefined();
expect($scope.count).toBe(5);
});
it('should change counter value', function() {
$scope.incrementCount();
expect($scope.count).toBe(6);
});
});
AngularJS service unit testing:
appSrvc.js:
app.factory('appSrvc', function () {
return {
countriesList: [
{name: 'England', capital: 'London'},
{name: 'Germany', capital: 'Berlin'},
{name: 'Italy', capital: 'Rome'},
{name: 'Spain', capital: 'Madrid'}
],
checkCountry: function(countryName) {
var countryInList = false;
angular.forEach(this.countriesList, function (value, index) {
if (value.name === countryName) {
countryInList = true; // country is in the list
}
});
return countryInList;
}
};
}); |
app.factory('appSrvc', function () {
return {
countriesList: [
{name: 'England', capital: 'London'},
{name: 'Germany', capital: 'Berlin'},
{name: 'Italy', capital: 'Rome'},
{name: 'Spain', capital: 'Madrid'}
],
checkCountry: function(countryName) {
var countryInList = false;
angular.forEach(this.countriesList, function (value, index) {
if (value.name === countryName) {
countryInList = true; // country is in the list
}
});
return countryInList;
}
};
});
appSrvc.spec.js:
describe('appSrvc', function(){
var appSrvc, $scope;
beforeEach(function() { // executed before each 'it' is run
module('app'); // load the module
// The _underscores_ are a convenience thing
// so you can have your variable name be the
// same as your injected service.
inject(function(_appSrvc_) { // inject service for testing
appSrvc = _appSrvc_;
});
});
it('should provide a countriesList array property', function () {
expect(appSrvc.checkCountry).toBeDefined();
expect(appSrvc.countriesList instanceof Array).toBe(true);
});
it('should provide a myMethod function', function () {
expect(appSrvc.checkCountry).toBeDefined();
expect(typeof appSrvc.checkCountry).toBe('function');
});
it('should pass all test cases', function () {
var i, valid, invalid;
// test cases - testing for success
var validCountries = [
'England',
'Germany'
];
// test cases - testing for failure
var invalidCountries = [
'123',
''
];
// loop through arrays of test cases
for (i in validCountries) {
valid = appSrvc.checkCountry(validCountries[i]);
expect(valid).toBeTruthy();
}
for (i in invalidCountries) {
invalid = appSrvc.checkCountry(invalidCountries[i]);
expect(invalid).toBeFalsy();
}
});
}); |
describe('appSrvc', function(){
var appSrvc, $scope;
beforeEach(function() { // executed before each 'it' is run
module('app'); // load the module
// The _underscores_ are a convenience thing
// so you can have your variable name be the
// same as your injected service.
inject(function(_appSrvc_) { // inject service for testing
appSrvc = _appSrvc_;
});
});
it('should provide a countriesList array property', function () {
expect(appSrvc.checkCountry).toBeDefined();
expect(appSrvc.countriesList instanceof Array).toBe(true);
});
it('should provide a myMethod function', function () {
expect(appSrvc.checkCountry).toBeDefined();
expect(typeof appSrvc.checkCountry).toBe('function');
});
it('should pass all test cases', function () {
var i, valid, invalid;
// test cases - testing for success
var validCountries = [
'England',
'Germany'
];
// test cases - testing for failure
var invalidCountries = [
'123',
''
];
// loop through arrays of test cases
for (i in validCountries) {
valid = appSrvc.checkCountry(validCountries[i]);
expect(valid).toBeTruthy();
}
for (i in invalidCountries) {
invalid = appSrvc.checkCountry(invalidCountries[i]);
expect(invalid).toBeFalsy();
}
});
});
AngularJS directive unit testing:
appDrct.js:
app.directive('classWell', function() {
return function(scope, element) {
element.addClass('well');
}
}); |
app.directive('classWell', function() {
return function(scope, element) {
element.addClass('well');
}
});
appDrct.spec.js:
describe('appDrct', function() {
var element, $scope;
beforeEach(function() { // executed before each 'it' is run
module('app'); // load the module
inject(function($compile, $rootScope) { // inject directive for testing
$scope = $rootScope;
element = angular.element('<div class-well>{{2 + 2}}</div>');
$compile(element)($rootScope);
})
});
it('should equal 4', function() {
$scope.$digest();
expect(element.html()).toBe('4');
});
it('should add a class of well', function() {
expect(element.hasClass('well')).toBe(true);
});
}); |
describe('appDrct', function() {
var element, $scope;
beforeEach(function() { // executed before each 'it' is run
module('app'); // load the module
inject(function($compile, $rootScope) { // inject directive for testing
$scope = $rootScope;
element = angular.element('<div class-well>{{2 + 2}}</div>');
$compile(element)($rootScope);
})
});
it('should equal 4', function() {
$scope.$digest();
expect(element.html()).toBe('4');
});
it('should add a class of well', function() {
expect(element.hasClass('well')).toBe(true);
});
});
AngularJS filter unit testing:
appFltr.js:
app.filter('reverse', function(){
return function(text){
return text.split('').reverse().join('');
}
}); |
app.filter('reverse', function(){
return function(text){
return text.split('').reverse().join('');
}
});
appFltr.spec.js:
describe('reverseFltr', function() {
var reverseFltr;
beforeEach(function() { // executed before each 'it' is run
module('app'); // load the module
inject(function($filter) { // inject filter for testing
reverseFltr = $filter('reverse');
});
});
it('should have reverseFltr filter toBeDefined', function() {
expect(reverseFltr).toBeDefined();
//expect(!!reverseFltr).toBe(true);
});
it('should reverse the text', function () {
expect(reverseFltr('abc')).toEqual('cba');
});
}); |
describe('reverseFltr', function() {
var reverseFltr;
beforeEach(function() { // executed before each 'it' is run
module('app'); // load the module
inject(function($filter) { // inject filter for testing
reverseFltr = $filter('reverse');
});
});
it('should have reverseFltr filter toBeDefined', function() {
expect(reverseFltr).toBeDefined();
//expect(!!reverseFltr).toBe(true);
});
it('should reverse the text', function () {
expect(reverseFltr('abc')).toEqual('cba');
});
});
Karma config:
karma.conf.js:
// Karma configuration
// http://karma-runner.github.io/0.12/config/configuration-file.html
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: [
'js/vendor/angular.js',
//'js/vendor/*.js',
'js/vendor/angular-mocks.js',
// scripts
'js/app.js',
'js/app*.js',
//'js/appCtrl.js',
//'js/appDctv.js',
'tests/**/*spec.js'
//'tests/unit/appCtrl.spec.js'
//'tests/unit/appDctv.spec.js'
],
// list of files to exclude
exclude: [
'tests/coverage/**/*.js'
],
// preprocess matching files before serving them to the browser
// available preprocessors: https://npmjs.org/browse/keyword/karma-preprocessor
preprocessors: {
'js/**/*.js': ['coverage']//,
// 'js/config/**/*.js' : ['coverage']
},
// test results reporter to use
// possible values: 'dots', 'progress'
// available reporters: https://npmjs.org/browse/keyword/karma-reporter
reporters: ['progress', 'coverage', 'html'],
// 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: true,
coverageReporter: {
type: 'html',
dir: 'tests/coverage/'
},
htmlReporter: {
outputDir: 'tests/karma_html_report',
templatePath: __dirname + '/jasmine_template.html'
},
// start these browsers
// available browser launchers: https://npmjs.org/browse/keyword/karma-launcher
browsers: ['PhantomJS'],
// Continuous Integration mode
// if true, Karma captures browsers, runs the tests and exits
singleRun: false
});
}; |
// Karma configuration
// http://karma-runner.github.io/0.12/config/configuration-file.html
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: [
'js/vendor/angular.js',
//'js/vendor/*.js',
'js/vendor/angular-mocks.js',
// scripts
'js/app.js',
'js/app*.js',
//'js/appCtrl.js',
//'js/appDctv.js',
'tests/**/*spec.js'
//'tests/unit/appCtrl.spec.js'
//'tests/unit/appDctv.spec.js'
],
// list of files to exclude
exclude: [
'tests/coverage/**/*.js'
],
// preprocess matching files before serving them to the browser
// available preprocessors: https://npmjs.org/browse/keyword/karma-preprocessor
preprocessors: {
'js/**/*.js': ['coverage']//,
// 'js/config/**/*.js' : ['coverage']
},
// test results reporter to use
// possible values: 'dots', 'progress'
// available reporters: https://npmjs.org/browse/keyword/karma-reporter
reporters: ['progress', 'coverage', 'html'],
// 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: true,
coverageReporter: {
type: 'html',
dir: 'tests/coverage/'
},
htmlReporter: {
outputDir: 'tests/karma_html_report',
templatePath: __dirname + '/jasmine_template.html'
},
// start these browsers
// available browser launchers: https://npmjs.org/browse/keyword/karma-launcher
browsers: ['PhantomJS'],
// Continuous Integration mode
// if true, Karma captures browsers, runs the tests and exits
singleRun: false
});
};
Grunt config:
Gruntfile.js:
module.exports = function(grunt) {
var JSfiles = ['js/*.js'];
// Project configuration.
grunt.initConfig({
// pkg: grunt.file.readJSON('package.json'),
appConfig: grunt.file.readJSON('package.json'), // app.json
html2js: {
// options: {
// base: 'VersionedResources'
// },
main: {
src: ['js/**/*.html'],
dest: 'js/config/templates.js'
}
},
jshint: {
all: JSfiles,
options: { // http://www.jshint.com/docs/options/
globalstrict: false, // removes all 'use strict' errors
curly: true, // requires you to always put curly braces around blocks in loops and conditionals
eqeqeq: true, // prohibits the use of == and != in favor of === and !==
freeze: true, // prohibits overwriting prototypes of native objects such as Array, Date and so on
indent: 4, // enforces specific tab width for your code
latedef: true, // prohibits the use of a variable before it was defined
quotmark: 'single', // enforces to use 'single' or 'double' quotes
maxdepth: 4, // lets you control how nested do you want your blocks to be
maxlen: 200, // lets you set the maximum length of a line
eqnull: true, // option suppresses warnings about == null comparisons
browser: true, // defines globals exposed by modern browsers
globals: {
jQuery: true // defines globals exposed by the jQuery JavaScript library
}
}
},
eslint: {
all: {
options: { // https://github.com/eslint/eslint/blob/master/docs/rules/README.md
config: 'eslint.json',
rulesDir: './'
},
src: JSfiles
}
},
plato: {
unittesting: {
files: {
'reports': JSfiles
}
}
},
karma: {
unit: {
configFile: 'tests/config/karma.conf.js'
},
e2e: {
configFile: 'tests/config/e2e.js'
}
},
watch: {
js: {
files: JSfiles,
tasks: ['jshint', 'eslint']
}
}
});
// Load the plugins
grunt.loadNpmTasks('grunt-contrib-less');
grunt.loadNpmTasks('grunt-lesslint');
grunt.loadNpmTasks('grunt-contrib-csslint');
grunt.loadNpmTasks('grunt-contrib-jshint');
grunt.loadNpmTasks('eslint-grunt');
grunt.loadNpmTasks('grunt-contrib-watch');
grunt.loadNpmTasks('grunt-karma');
grunt.loadNpmTasks('grunt-plato');
grunt.loadNpmTasks('grunt-html2js');
// Default task
grunt.registerTask('default', ['less']);
grunt.registerTask('js', ['jshint', 'eslint']);
grunt.registerTask('tests', ['karma:unit']);
}; |
module.exports = function(grunt) {
var JSfiles = ['js/*.js'];
// Project configuration.
grunt.initConfig({
// pkg: grunt.file.readJSON('package.json'),
appConfig: grunt.file.readJSON('package.json'), // app.json
html2js: {
// options: {
// base: 'VersionedResources'
// },
main: {
src: ['js/**/*.html'],
dest: 'js/config/templates.js'
}
},
jshint: {
all: JSfiles,
options: { // http://www.jshint.com/docs/options/
globalstrict: false, // removes all 'use strict' errors
curly: true, // requires you to always put curly braces around blocks in loops and conditionals
eqeqeq: true, // prohibits the use of == and != in favor of === and !==
freeze: true, // prohibits overwriting prototypes of native objects such as Array, Date and so on
indent: 4, // enforces specific tab width for your code
latedef: true, // prohibits the use of a variable before it was defined
quotmark: 'single', // enforces to use 'single' or 'double' quotes
maxdepth: 4, // lets you control how nested do you want your blocks to be
maxlen: 200, // lets you set the maximum length of a line
eqnull: true, // option suppresses warnings about == null comparisons
browser: true, // defines globals exposed by modern browsers
globals: {
jQuery: true // defines globals exposed by the jQuery JavaScript library
}
}
},
eslint: {
all: {
options: { // https://github.com/eslint/eslint/blob/master/docs/rules/README.md
config: 'eslint.json',
rulesDir: './'
},
src: JSfiles
}
},
plato: {
unittesting: {
files: {
'reports': JSfiles
}
}
},
karma: {
unit: {
configFile: 'tests/config/karma.conf.js'
},
e2e: {
configFile: 'tests/config/e2e.js'
}
},
watch: {
js: {
files: JSfiles,
tasks: ['jshint', 'eslint']
}
}
});
// Load the plugins
grunt.loadNpmTasks('grunt-contrib-less');
grunt.loadNpmTasks('grunt-lesslint');
grunt.loadNpmTasks('grunt-contrib-csslint');
grunt.loadNpmTasks('grunt-contrib-jshint');
grunt.loadNpmTasks('eslint-grunt');
grunt.loadNpmTasks('grunt-contrib-watch');
grunt.loadNpmTasks('grunt-karma');
grunt.loadNpmTasks('grunt-plato');
grunt.loadNpmTasks('grunt-html2js');
// Default task
grunt.registerTask('default', ['less']);
grunt.registerTask('js', ['jshint', 'eslint']);
grunt.registerTask('tests', ['karma:unit']);
};
package.json:
{
"name": "AngularJS unit testing",
"description": "AngularJS unit testing",
"version": "1.0.0",
"devDependencies": {
"underscore": "latest",
"grunt": "latest",
"grunt-contrib-watch": "latest",
"grunt-contrib-less": "latest",
"grunt-lesslint": "latest",
"grunt-contrib-csslint": "latest",
"grunt-contrib-jshint": "latest",
"eslint-grunt": "latest",
"grunt-contrib-copy": "latest",
"karma-script-launcher": "latest",
"karma-chrome-launcher": "latest",
"karma-jasmine": "latest",
"karma-phantomjs-launcher": "latest",
"karma": "latest",
"karma-story-reporter": "latest",
"grunt-karma": "latest",
"karma-coverage": "latest",
"karma-sauce-launcher": "latest",
"karma-html-reporter": "^0.2.3",
"grunt-plato": "^1.0.0",
"grunt-html2js": "^0.2.7"
}
} |
{
"name": "AngularJS unit testing",
"description": "AngularJS unit testing",
"version": "1.0.0",
"devDependencies": {
"underscore": "latest",
"grunt": "latest",
"grunt-contrib-watch": "latest",
"grunt-contrib-less": "latest",
"grunt-lesslint": "latest",
"grunt-contrib-csslint": "latest",
"grunt-contrib-jshint": "latest",
"eslint-grunt": "latest",
"grunt-contrib-copy": "latest",
"karma-script-launcher": "latest",
"karma-chrome-launcher": "latest",
"karma-jasmine": "latest",
"karma-phantomjs-launcher": "latest",
"karma": "latest",
"karma-story-reporter": "latest",
"grunt-karma": "latest",
"karma-coverage": "latest",
"karma-sauce-launcher": "latest",
"karma-html-reporter": "^0.2.3",
"grunt-plato": "^1.0.0",
"grunt-html2js": "^0.2.7"
}
}
eslint.json:
{
"rules": {
"no-alert": 1,
"no-caller": 1,
"no-bitwise": 1,
"no-console": 1,
"no-debugger": 1,
"no-empty": 1,
"no-eval": 1,
"no-floating-decimal": 1,
"no-with": 1,
"no-unreachable": 1,
"no-undef": 0,
"no-undef-init": 1,
"no-octal": 1,
"no-new-wrappers": 1,
"no-new": 1,
"no-underscore-dangle": 0,
"strict": 0,
"smarter-eqeqeq": 0,
"brace-style": 1,
"camelcase": 0,
"curly": 1,
"eqeqeq": 1,
"new-parens": 1,
"guard-for-in": 1,
"new-cap": 1,
"quotes": [2, "single", "avoid-escape"],
"quote-props": 0,
"semi": 1,
"use-isnan": 1,
"no-array-constructor": 1,
"no-new-object": 1
}
} |
{
"rules": {
"no-alert": 1,
"no-caller": 1,
"no-bitwise": 1,
"no-console": 1,
"no-debugger": 1,
"no-empty": 1,
"no-eval": 1,
"no-floating-decimal": 1,
"no-with": 1,
"no-unreachable": 1,
"no-undef": 0,
"no-undef-init": 1,
"no-octal": 1,
"no-new-wrappers": 1,
"no-new": 1,
"no-underscore-dangle": 0,
"strict": 0,
"smarter-eqeqeq": 0,
"brace-style": 1,
"camelcase": 0,
"curly": 1,
"eqeqeq": 1,
"new-parens": 1,
"guard-for-in": 1,
"new-cap": 1,
"quotes": [2, "single", "avoid-escape"],
"quote-props": 0,
"semi": 1,
"use-isnan": 1,
"no-array-constructor": 1,
"no-new-object": 1
}
}