<!DOCTYPE html>
<html ng-app="Ratings">
<head>
<meta charset="utf-8" />
<title>AngularJS test</title>
<link data-require="jasmine" data-semver="1.3.1" rel="stylesheet" href="//cdn.jsdelivr.net/jasmine/1.3.1/jasmine.css" />
<script data-require="json2" data-semver="0.0.2012100-8" src="//cdnjs.cloudflare.com/ajax/libs/json2/20121008/json2.js"></script>
<script data-require="jasmine" data-semver="1.3.1" src="//cdn.jsdelivr.net/jasmine/1.3.1/jasmine.js"></script>
<script data-require="jasmine" data-semver="1.3.1" src="//cdn.jsdelivr.net/jasmine/1.3.1/jasmine-html.js"></script>
<script data-require="angular.js" data-semver="1.4.0-beta.5" src="http://code.angularjs.org/1.4.0-beta.5/angular.js"></script>
<script data-require="angular-mocks" data-semver="1.4.0-beta.5" src="http://code.angularjs.org/1.4.0-beta.5/angular-mocks.js"></script>
<link rel="stylesheet" href="rating.directive.css" />
<script src="rating.directive.js"></script>
<script src="rating.directive.spec.js"></script>
<script src="jasmineBootstrap.js"></script>
<!-- bootstraps Jasmine -->
</head>
<body>
<div id="HTMLReporter" class="jasmine_reporter"></div>
</body>
</html>
Angular.js Directive Unit Testing with Custom Matchers in Jasmine.
Updated AngularJS to 1.4.0-beta.5 and Jasmine to 1.3.1
'use strict';
describe('Directive: rater', function () {
// load the directive's module and view
beforeEach(module('Ratings'));
var element, scope, starRating, getStar;
beforeEach(function () {
/* Function counts number of filled stars */
starRating= function (element) {
var elementLength = element.text().length - 1,
starCount = 0;
for (var i = 0; i < elementLength; i++) {
if (!element.find('li').eq(i).hasClass('filled')) {
starCount = i;
break;
}
}
return starCount;
};
/*
getStar = function (element, number) {
return element.find('li').eq(number - 1);
};
*/
/* Custom Machers Here */
this.addMatchers({
toBeStarCount: function (starCount) {
this.message = function () {
return "Expected " + starCount + " stars, but counted " + this.actual + ".";
};
return this.actual === starCount;
}
});
});
describe('default rater', function () {
beforeEach(inject(function ($rootScope, $compile) {
scope = $rootScope.$new();
element = angular.element('<rater></rater>');
element = $compile(element)(scope);
scope.$digest();
}));
it('should display the element on the page with stars', function () {
expect(element).toBeDefined();
expect(element.text()).toContain('★');
});
it('should have a max of 5 stars', function () {
expect(element.text()).toBe('★★★★★');
});
it('should show a default element with a 3 out of 5 star rating', function () {
expect(starRating(element)).toBeStarCount(3);
//expect(element.find('li').eq(0)).toHaveClass('filled'); // 1st star filled
//expect(element.find('li').eq(2)).toHaveClass('filled'); // 3rd star filled
//expect(element.find('li').eq(3)).not.toHaveClass('filled'); // 4th star not filled
});
});
describe('rating with attributes', function () {
beforeEach(inject(function ($rootScope, $compile) {
scope = $rootScope.$new();
element = angular.element('<rater icon="X" rating="7" max="10"></rater>');
element = $compile(element)(scope);
scope.$digest();
}));
it('should change the icons to "X"\'s', function () {
expect(element.text()).toContain("X");
})
it('should set the max to 10', function () {
expect(element.text().length).toBe(10);
});
it('should set the rating to 7', function () {
expect(starRating(element)).toBeStarCount(7);
});
});
});
'use strict';
angular.module('Ratings', [])
.directive('rater', function () {
return {
restrict: 'E',
scope: {
icon: '@',
max: '=?',
rating: '=?',
readOnly: '@',
functionOnSelect: '@'
},
controller: function ($scope) {
$scope.max = $scope.max || 5;
$scope.rating = $scope.rating || Math.round($scope.max)/2;
// function-on-select
$scope.runFunction = function (newRating) {
alert('Adding rating of ' + newRating + ' to server.');
};
},
template: '<ul class="rating">' +
'<li ng-repeat="item in items" ng-class="item" ng-click="toggle($index)">' +
'{{icon}}' +
'</li>' +
'</ul>',
link: function (scope, element, attrs) {
var update = function () {
scope.items = [];
for (var i = 0; i < scope.max; i++) {
scope.items.push({filled: i < scope.rating});
}
scope.icon = scope.icon || '★';
// any properties here will update on click
};
scope.toggle = function (index) {
if (scope.readOnly && scope.readOnly === 'true') {
return;
}
scope.rating = index + 1;
if (attrs.functionOnSelect) {
scope.runFunction(scope.rating);
}
};
scope.$watch('rating', function (oldVal, newVal) {
if (newVal) { update(); }
});
}
};
});
/* Taken from this demo: http://www.befundoo.com/university/tutorials/angularjs-directives-tutorial/ */
.rating{
color: #a9a9a9;
margin: 0;
padding: 0;
}
ul.rating {
display: inline-block;
}
.rating li {
list-style-type: none;
display: inline-block;
padding: 1px;
text-align: center;
font-weight: bold;
cursor: pointer;
}
.rating .filled {
color: #21568b;
}
module.exports = function(config) {
'use strict';
config.set({
// base path, that will be used to resolve files and exclude
basePath: '',
frameworks: ['jasmine'],
plugins: [
'karma-jasmine',
'karma-phantomjs-launcher'
],
// list of files / patterns to load in the browser
files: [
'bower_components/angular/angular.js',
'bower_components/angular-mocks/angular-mocks.js',
'rating.directive.js',
'rating.directive.spec.js'
],
// list of files to exclude
exclude: [],
// test results reporter to use
// possible values: 'dots', 'progress', 'junit'
reporters: ['progress'],
// web server port
port: 9876,
// cli runner port
runnerPort: 9100,
// enable / disable colors in the output (reporters and logs)
colors: true,
// 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'],
// If browser does not capture in given timeout [ms], kill it
captureTimeout: 60000,
// Continuous Integration mode
// if true, it capture browsers, run tests and exit
singleRun: true
});
};
{
"name": "sampleProvider Test Pattern",
"version": "1.0.0",
"dependencies": {
"angular": "~1.2.0"
},
"devDependencies": {
"angular-mocks": "~1.2.0",
"angular-scenario": "~1.2.0"
}
}
{
"name": "sampleProvider",
"description": "Shows an example of how you can test your AngularJS providers.",
"version": "1.0.0",
"dependencies": {},
"devDependencies": {
"karma": "~0.10",
"karma-jasmine": "~0.1",
"karma-phantomjs-launcher": "~0.1.1"
},
"engines": {
"node": ">=0.10.0"
}
}
(function() {
var jasmineEnv = jasmine.getEnv();
jasmineEnv.updateInterval = 250;
/**
Create the `HTMLReporter`, which Jasmine calls to provide results of each spec and each suite. The Reporter is responsible for presenting results to the user.
*/
var htmlReporter = new jasmine.HtmlReporter();
jasmineEnv.addReporter(htmlReporter);
/**
Delegate filtering of specs to the reporter. Allows for clicking on single suites or specs in the results to only run a subset of the suite.
*/
jasmineEnv.specFilter = function(spec) {
return htmlReporter.specFilter(spec);
};
/**
Run all of the tests when the page finishes loading - and make sure to run any previous `onload` handler
### Test Results
Scroll down to see the results of all of these specs.
*/
var currentWindowOnload = window.onload;
window.onload = function() {
if (currentWindowOnload) {
currentWindowOnload();
}
//document.querySelector('.version').innerHTML = jasmineEnv.versionString();
execJasmine();
};
function execJasmine() {
jasmineEnv.execute();
}
})();