I'm having trouble getting started using Jasmine and AngularJS. I've tried to create a minimal example, largely derived from the angular docs:
I have a module and a controller in testme.js:
(function(){
"use strict";
let cont = function($scope) {
$scope.data = 99;
};
let app = angular.module('Test', []);
app.controller('Cont', cont);
}());
I have a test TestSpec.js
"use strict";
console.log('launching test...');
describe('Test a test', function(){
console.log('in describe...');
beforeEach(angular.module('Test'));
it('should create a number', inject(function($controller){
let scope = {};
console.log('in test...');
let ctrl = $controller('Cont', {$scope:scope}); // fails here
console.log('got controller...');
expect(scope.data).toBe(99);
console.log('test completed...');
}));
});
And a SpecRunner.html
<!DOCTYPE html>
<html data-ng-app="Test">
<head>
<link rel="shortcut icon" type="image/png" href="jasmine/lib/jasmine-2.4.1/jasmine_favicon.png">
<link rel="stylesheet" href="jasmine/lib/jasmine-2.4.1/jasmine.css">
<script src="jasmine/lib/jasmine-2.4.1/jasmine.js"></script>
<script src="jasmine/lib/jasmine-2.4.1/jasmine-html.js"></script>
<script src="jasmine/lib/jasmine-2.4.1/boot.js"></script>
<script src="scripts/angular.js"></script>
<script src="scripts/angular-mocks.js"></script>
<script src="scripts/testme.js"></script>
<script src="test/TestSpec.js"></script>
</head>
<body data-ng-controller="Cont">
<h1>Value is {{data}}</h1>
</body>
</html>
It seems like it starts up, and I get the messages
launching test...
in describe...
in test...
but it fails trying to execute the line I marked as // fails here, in TestSpec.js. This tells me that I'm not managing to retrieve the controller. Needless to say, I don't understand why (there are many questions on this topic, but they all seem not applicable here, as far as I can see).
Oh, and in case it's relevant, I'm using Angular 1.5:
The error is here
beforeEach(angular.module('Test'));
It should be module.
You should create a new scope as the child of $rootScope. Replace the line to create scope with below in your test:
beforeEach(angular.mock.module('Test'));
it('should create a number', inject(function($controller, $rootScope){
let scope = $rootScope.$new();
console.log('in test...');
let ctrl = $controller('Cont', {$scope:scope});
console.log('got controller...');
expect(scope.data).toBe(99);
console.log('test completed...');
}));
Also you should not use angular.module but angular.mock.module to refer to modules
You're not injecting your controller dependencies, and your structure is a little off. You don't need to assign block level variables.
(function(){
"use strict";
angular.module('Test', [])
.controller('Cont', cont);
Cont.$inject = ['$scope'];
function cont($scope) {
$scope.data = 99;
};
}());
Related
Currently trying to check very simple angular variables and functions. I cannot get even the simplest to work. This is my first time using it, and I need to use the older version as my lecturer requires it.
In the app.js I have many controllers but im only starting with one controller, with one variable assigned to a string:
var app = angular.module('myApp', ['ngRoute']);
blah blah blah etc etc
app.controller('HomeController', function ($scope) {
$scope.name = 'Batman';
}
In the runner.html file I have the following:
<script src="lib/jasmine-2.2.0/jasmine.js"></script>
<script src="lib/jasmine-2.2.0/jasmine-html.js"></script>
<script src="lib/jasmine-2.2.0/boot.js"></script>
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.4.8/angular.min.js"></script>
<script src='https://code.angularjs.org/1.4.8/angular-mocks.js'></script>
<!-- include source files here... -->
<script src="../js/main.js"></script>
<!-- include spec files here... -->
<script src="spec/test.js"></script>
In the test.js i have the following:
describe('myApp', function () {
var scope, controller;
beforeEach(angular.mock.module('myApp'));
beforeEach(angular.mock.inject(function($controller,$rootScope){
scope = $rootScope.$new();
$controller('HomeController', {$scope: scope});
}));
it('sets the name', function () {
expect(scope.name).toBe('Batman');
});
});
I then get the following error when I run the test:
Error: [$injector:modulerr]
EDIT
It appears to be the angular routing causing the problem. When I removed the route module 'ngRoute' it appears to function correctly. Is there is method to using jasmine with routing?
The problem with this was you were not having angular-route library included despite having it as a module dependency (angular.module('myApp', ['ngRoute'])).
I added as the following along with other libraries:
<script src="https://cdnjs.cloudflare.com/ajax/libs/angular.js/1.4.8/angular-route.min.js"></script>
And it's working!
Here's working plunker
If I have a utility function foo that I want to be able to call from anywhere inside of my ng-app declaration. Is there someway I can make it globally accessible in my module setup or do I need to add it to the scope in every controller?
You basically have two options, either define it as a service, or place it on your root scope. I would suggest that you make a service out of it to avoid polluting the root scope. You create a service and make it available in your controller like this:
<!doctype html>
<html ng-app="myApp">
<head>
<script src="http://code.jquery.com/jquery-1.9.1.min.js"></script>
<script src="http://code.angularjs.org/1.1.2/angular.min.js"></script>
<script type="text/javascript">
var myApp = angular.module('myApp', []);
myApp.factory('myService', function() {
return {
foo: function() {
alert("I'm foo!");
}
};
});
myApp.controller('MainCtrl', ['$scope', 'myService', function($scope, myService) {
$scope.callFoo = function() {
myService.foo();
}
}]);
</script>
</head>
<body ng-controller="MainCtrl">
<button ng-click="callFoo()">Call foo</button>
</body>
</html>
If that's not an option for you, you can add it to the root scope like this:
<!doctype html>
<html ng-app="myApp">
<head>
<script src="http://code.jquery.com/jquery-1.9.1.min.js"></script>
<script src="http://code.angularjs.org/1.1.2/angular.min.js"></script>
<script type="text/javascript">
var myApp = angular.module('myApp', []);
myApp.run(function($rootScope) {
$rootScope.globalFoo = function() {
alert("I'm global foo!");
};
});
myApp.controller('MainCtrl', ['$scope', function($scope){
}]);
</script>
</head>
<body ng-controller="MainCtrl">
<button ng-click="globalFoo()">Call global foo</button>
</body>
</html>
That way, all of your templates can call globalFoo() without having to pass it to the template from the controller.
You can also combine them I guess:
<!doctype html>
<html ng-app="myApp">
<head>
<script src="http://code.jquery.com/jquery-1.9.1.min.js"></script>
<script src="http://code.angularjs.org/1.1.2/angular.min.js"></script>
<script type="text/javascript">
var myApp = angular.module('myApp', []);
myApp.factory('myService', function() {
return {
foo: function() {
alert("I'm foo!");
}
};
});
myApp.run(function($rootScope, myService) {
$rootScope.appData = myService;
});
myApp.controller('MainCtrl', ['$scope', function($scope){
}]);
</script>
</head>
<body ng-controller="MainCtrl">
<button ng-click="appData.foo()">Call foo</button>
</body>
</html>
Though the first approach is advocated as 'the angular like' approach, I feel this adds overheads.
Consider if I want to use this myservice.foo function in 10 different controllers. I will have to specify this 'myService' dependency and then $scope.callFoo scope property in all ten of them. This is simply a repetition and somehow violates the DRY principle.
Whereas, if I use the $rootScope approach, I specify this global function gobalFoo only once and it will be available in all my future controllers, no matter how many.
AngularJs has "Services" and "Factories" just for problems like yours.These are used to have something global between Controllers, Directives, Other Services or any other angularjs components..You can defined functions, store data, make calculate functions or whatever you want inside Services and use them in AngularJs Components as Global.like
angular.module('MyModule', [...])
.service('MyService', ['$http', function($http){
return {
users: [...],
getUserFriends: function(userId){
return $http({
method: 'GET',
url: '/api/user/friends/' + userId
});
}
....
}
}])
if you need more
Find More About Why We Need AngularJs Services and Factories
I'm a bit newer to Angular but what I found useful to do (and pretty simple) is I made a global script that I load onto my page before the local script with global variables that I need to access on all pages anyway. In that script, I created an object called "globalFunctions" and added the functions that I need to access globally as properties. e.g. globalFunctions.foo = myFunc();. Then, in each local script, I wrote $scope.globalFunctions = globalFunctions; and I instantly have access to any function I added to the globalFunctions object in the global script.
This is a bit of a workaround and I'm not sure it helps you but it definitely helped me as I had many functions and it was a pain adding all of them to each page.
If I have a utility function foo that I want to be able to call from anywhere inside of my ng-app declaration. Is there someway I can make it globally accessible in my module setup or do I need to add it to the scope in every controller?
You basically have two options, either define it as a service, or place it on your root scope. I would suggest that you make a service out of it to avoid polluting the root scope. You create a service and make it available in your controller like this:
<!doctype html>
<html ng-app="myApp">
<head>
<script src="http://code.jquery.com/jquery-1.9.1.min.js"></script>
<script src="http://code.angularjs.org/1.1.2/angular.min.js"></script>
<script type="text/javascript">
var myApp = angular.module('myApp', []);
myApp.factory('myService', function() {
return {
foo: function() {
alert("I'm foo!");
}
};
});
myApp.controller('MainCtrl', ['$scope', 'myService', function($scope, myService) {
$scope.callFoo = function() {
myService.foo();
}
}]);
</script>
</head>
<body ng-controller="MainCtrl">
<button ng-click="callFoo()">Call foo</button>
</body>
</html>
If that's not an option for you, you can add it to the root scope like this:
<!doctype html>
<html ng-app="myApp">
<head>
<script src="http://code.jquery.com/jquery-1.9.1.min.js"></script>
<script src="http://code.angularjs.org/1.1.2/angular.min.js"></script>
<script type="text/javascript">
var myApp = angular.module('myApp', []);
myApp.run(function($rootScope) {
$rootScope.globalFoo = function() {
alert("I'm global foo!");
};
});
myApp.controller('MainCtrl', ['$scope', function($scope){
}]);
</script>
</head>
<body ng-controller="MainCtrl">
<button ng-click="globalFoo()">Call global foo</button>
</body>
</html>
That way, all of your templates can call globalFoo() without having to pass it to the template from the controller.
You can also combine them I guess:
<!doctype html>
<html ng-app="myApp">
<head>
<script src="http://code.jquery.com/jquery-1.9.1.min.js"></script>
<script src="http://code.angularjs.org/1.1.2/angular.min.js"></script>
<script type="text/javascript">
var myApp = angular.module('myApp', []);
myApp.factory('myService', function() {
return {
foo: function() {
alert("I'm foo!");
}
};
});
myApp.run(function($rootScope, myService) {
$rootScope.appData = myService;
});
myApp.controller('MainCtrl', ['$scope', function($scope){
}]);
</script>
</head>
<body ng-controller="MainCtrl">
<button ng-click="appData.foo()">Call foo</button>
</body>
</html>
Though the first approach is advocated as 'the angular like' approach, I feel this adds overheads.
Consider if I want to use this myservice.foo function in 10 different controllers. I will have to specify this 'myService' dependency and then $scope.callFoo scope property in all ten of them. This is simply a repetition and somehow violates the DRY principle.
Whereas, if I use the $rootScope approach, I specify this global function gobalFoo only once and it will be available in all my future controllers, no matter how many.
AngularJs has "Services" and "Factories" just for problems like yours.These are used to have something global between Controllers, Directives, Other Services or any other angularjs components..You can defined functions, store data, make calculate functions or whatever you want inside Services and use them in AngularJs Components as Global.like
angular.module('MyModule', [...])
.service('MyService', ['$http', function($http){
return {
users: [...],
getUserFriends: function(userId){
return $http({
method: 'GET',
url: '/api/user/friends/' + userId
});
}
....
}
}])
if you need more
Find More About Why We Need AngularJs Services and Factories
I'm a bit newer to Angular but what I found useful to do (and pretty simple) is I made a global script that I load onto my page before the local script with global variables that I need to access on all pages anyway. In that script, I created an object called "globalFunctions" and added the functions that I need to access globally as properties. e.g. globalFunctions.foo = myFunc();. Then, in each local script, I wrote $scope.globalFunctions = globalFunctions; and I instantly have access to any function I added to the globalFunctions object in the global script.
This is a bit of a workaround and I'm not sure it helps you but it definitely helped me as I had many functions and it was a pain adding all of them to each page.
Why is $uibModalInstance unable to be injected here?
http://plnkr.co/edit/mi9Ytv0HaqE47ENod4Gn?p=preview
baseController.$inject = ['$uibModal', '$uibModalInstance'];
function baseController($uibModal, $uibModalInstance) {
self.ok = function () {
$uibModalInstance.close(self.selected.item);
};
self.cancel = function () {
$uibModalInstance.dismiss('cancel');
};
I am trying to access $uibModalInstance and if I try to inject it, I get an injection error.
If I don't inject it, then it's Undefined...
So LOTS of great statements here, but none of them quite solved the issue.
amg-argh got my plkr working with the way I had set up my injection
http://plnkr.co/edit/GXXmUosUEnEO3Tk0gUyp?p=preview
The biggest magic came from this edit here...
var childController = function ($scope, $uibModalInstance) {
$scope.ok = function () {
$uibModalInstance.close({ my: 'data' });
}
$scope.cancel = function () {
$uibModalInstance.dismiss();
}
}
self.open = function (size) {
var modalInstance = $uibModal.open({
animation: self.animationsEnabled,
templateUrl: 'help.html',
controller: childController,
size: size
});
};
+++++++++++++++++++++++++++++
I also want to make a note that this SHOULD be done later on as a Service, just as icfantv has pointed out. But I need to figure out the syntax for returning and consuming the modal's promise...
Thank you everyone for your help!!
Cheers!
I suspect the reason your code is failing is because you're trying to inject the modal instance into the controller whose responsibility it is to create the modal. It can't inject it because it doesn't exist yet. Try using separate controllers: one for opening the modal and another for processing the modal contents. You can see an example of this in the Javascript portion of our modal example.
TBH, I've never seen dependencies injected that way before. Is there some problem you're solving by doing that? Any reason you're not just using:
angular.module(...).controller('MyController', ['dep1', dep2', ..., MyControllerFunction]);
function MyControllerFunction(dep1, dep2) {...}
There were a few things wrong with your plunk example:
The order in which you load your scripts is important, you had:
<script data-require="angular.js#1.4.x" src="https://ajax.googleapis.com/ajax/libs/angularjs/1.4.7/angular.js" data-semver="1.4.7"></script>
<script src="app.module.js"></script>
<script src="childController.js"></script>
<script src="baseController.js"></script>
<script src="//ajax.googleapis.com/ajax/libs/angularjs/1.4.7/angular-animate.js"></script>
<script src="//angular-ui.github.io/bootstrap/ui-bootstrap-tpls-0.14.3.js"></script>
This will not work as app.module.js depends on the angular-ui-bootstrap.js and childController.js depends on angular-animate.js and childController.js depends on baseController.js You need to load the scripts in the following order:
<script data-require="angular.js#1.4.x" src="https://ajax.googleapis.com/ajax/libs/angularjs/1.4.7/angular.js" data-semver="1.4.7"></script>
<script src="//ajax.googleapis.com/ajax/libs/angularjs/1.4.7/angular-animate.js"></script>
<script src="//angular-ui.github.io/bootstrap/ui-bootstrap-tpls-0.14.3.js"></script>
<script src="app.module.js"></script>
<script src="baseController.js"></script>
<script src="childController.js"></script>
In childcontroller.js, you have:
angular
.module('app', ['ngAnimate'])
Which is recreating and overwriting the 'app' created in the app.module.js. You should inject the ngAnimate module in your app.module.js like so:
angular
.module('app', ['ngAnimate', 'ui.bootstrap']);
You don't need a reference to $uibModalInstance in your base controller, the:
var modalInstance = $uibModal.open({
animation: self.animationsEnabled,
templateUrl: 'help.html',
controller: 'BaseController',
size: size
});
gives you access to the modal you opened through 'var modalInstance' which has close and dismiss methods.
If I have a utility function foo that I want to be able to call from anywhere inside of my ng-app declaration. Is there someway I can make it globally accessible in my module setup or do I need to add it to the scope in every controller?
You basically have two options, either define it as a service, or place it on your root scope. I would suggest that you make a service out of it to avoid polluting the root scope. You create a service and make it available in your controller like this:
<!doctype html>
<html ng-app="myApp">
<head>
<script src="http://code.jquery.com/jquery-1.9.1.min.js"></script>
<script src="http://code.angularjs.org/1.1.2/angular.min.js"></script>
<script type="text/javascript">
var myApp = angular.module('myApp', []);
myApp.factory('myService', function() {
return {
foo: function() {
alert("I'm foo!");
}
};
});
myApp.controller('MainCtrl', ['$scope', 'myService', function($scope, myService) {
$scope.callFoo = function() {
myService.foo();
}
}]);
</script>
</head>
<body ng-controller="MainCtrl">
<button ng-click="callFoo()">Call foo</button>
</body>
</html>
If that's not an option for you, you can add it to the root scope like this:
<!doctype html>
<html ng-app="myApp">
<head>
<script src="http://code.jquery.com/jquery-1.9.1.min.js"></script>
<script src="http://code.angularjs.org/1.1.2/angular.min.js"></script>
<script type="text/javascript">
var myApp = angular.module('myApp', []);
myApp.run(function($rootScope) {
$rootScope.globalFoo = function() {
alert("I'm global foo!");
};
});
myApp.controller('MainCtrl', ['$scope', function($scope){
}]);
</script>
</head>
<body ng-controller="MainCtrl">
<button ng-click="globalFoo()">Call global foo</button>
</body>
</html>
That way, all of your templates can call globalFoo() without having to pass it to the template from the controller.
You can also combine them I guess:
<!doctype html>
<html ng-app="myApp">
<head>
<script src="http://code.jquery.com/jquery-1.9.1.min.js"></script>
<script src="http://code.angularjs.org/1.1.2/angular.min.js"></script>
<script type="text/javascript">
var myApp = angular.module('myApp', []);
myApp.factory('myService', function() {
return {
foo: function() {
alert("I'm foo!");
}
};
});
myApp.run(function($rootScope, myService) {
$rootScope.appData = myService;
});
myApp.controller('MainCtrl', ['$scope', function($scope){
}]);
</script>
</head>
<body ng-controller="MainCtrl">
<button ng-click="appData.foo()">Call foo</button>
</body>
</html>
Though the first approach is advocated as 'the angular like' approach, I feel this adds overheads.
Consider if I want to use this myservice.foo function in 10 different controllers. I will have to specify this 'myService' dependency and then $scope.callFoo scope property in all ten of them. This is simply a repetition and somehow violates the DRY principle.
Whereas, if I use the $rootScope approach, I specify this global function gobalFoo only once and it will be available in all my future controllers, no matter how many.
AngularJs has "Services" and "Factories" just for problems like yours.These are used to have something global between Controllers, Directives, Other Services or any other angularjs components..You can defined functions, store data, make calculate functions or whatever you want inside Services and use them in AngularJs Components as Global.like
angular.module('MyModule', [...])
.service('MyService', ['$http', function($http){
return {
users: [...],
getUserFriends: function(userId){
return $http({
method: 'GET',
url: '/api/user/friends/' + userId
});
}
....
}
}])
if you need more
Find More About Why We Need AngularJs Services and Factories
I'm a bit newer to Angular but what I found useful to do (and pretty simple) is I made a global script that I load onto my page before the local script with global variables that I need to access on all pages anyway. In that script, I created an object called "globalFunctions" and added the functions that I need to access globally as properties. e.g. globalFunctions.foo = myFunc();. Then, in each local script, I wrote $scope.globalFunctions = globalFunctions; and I instantly have access to any function I added to the globalFunctions object in the global script.
This is a bit of a workaround and I'm not sure it helps you but it definitely helped me as I had many functions and it was a pain adding all of them to each page.