AngularJS controller test failing with RequireJS - angularjs

My jasmine test (using karma) throws the error : Argument 'salesListController' is not a function, got undefined
After searching a lot this question seems close to my error, but this is unit test not e2e scenario test.
My test-main.js
(function (window, require) {
'use strict';
var file, requireModules;
requireModules = [];
for (file in window.__karma__.files) {
if (window.__karma__.files.hasOwnProperty(file)) {
// console.log('loaded file'+ file);
if (file.substring(file.length - 26, file.length) === 'salesListControllertest.js') {
console.log('Added file to testing..');
requireModules.push(file);
}
}
}
//requireModules.push('appModule');
//requireModules.push('mocks');
deps: requireModules,
require({
baseUrl: '',
paths:{
'angular': '/base/app/bower_components/angular/angular',
'angularResource': '/base/app/bower_components/angular-resource/angular-resource',
'angularMocks': '/base/app/bower_components/angular-mocks/angular-mocks',
'appModule': '/base/app/scripts/appModule',
'appRoutes':'/base/app/scripts/appRoutes',
'services/dependencyResolverFor':'/base/app/scripts/services/dependencyResolverFor',
'salesListController' : '/base/app/scripts/controllers/salesListController'
},
shim:{
'angular' :{
exports:'angular'
},
'appRoutes': {exports:'appRoutes'},
'services/dependencyResolverFor' : {exports:'services/dependencyResolverFor'},
'appModule': {
deps: ['appRoutes', 'services/dependencyResolverFor'],
exports: 'appModule'
},
'angularResource': {
deps: ['angular'],
exports: 'angularResource'
},
'angularMocks': {
deps: ['angularResource'],
exports: 'angularMocks'
} ,
'salesListController': {
deps: ['appModule'],
exports: 'salesListController'
}
}
}, requireModules, function () {
window.__karma__.start();
}, function (err) {
var failedModules = err.requireModules;
console.log("err", err);
if (failedModules && failedModules[0]) {
throw new Error("Module could not be loaded: " + failedModules);
} else {
throw new Error("unknown error:" + err);
}
}); }(window, require));
My example.spec
define(['appModule','angular', 'angularResource', 'angularMocks','salesListController'],
function(app, angular, angularResource, angularMocks, saleslstCtrl) {
describe('SalesListController1', function(){
beforeEach(module('AngularSampleBhoomiApp'));
var SalesListController, scope;
var sales = [{Customer:"A1",Number:1,Id:1},{Customer:"B1",Number:2,Id:2}];
beforeEach(inject(function($controller, $rootScope,$injector){
scope = $rootScope.$new();
SalesListController = $controller('salesListController', {$scope:scope, getAllSalesResolved:sales});
}));
it('should have 0 items when loaded', function(){
expect(scope.sales).toBeUndefined();
});
});});
My example controller
define(['appModule'], function(myApp){
function SalesLstCtrl(){
myApp.lazy.controller('salesListController' ,['$scope', 'getAllSalesResolved',
function ($scope, sales) {
console.log('before sales');
$scope.sales= sales;
console.log( $scope.sales.length);
}]); }
return SalesLstCtrl;});
Since I am using lazy loading I am resolving the dependencies like this :
define([], function()
{
return function(dependencies)
{
var definition =
{
resolver: ['$q','$rootScope', function($q, $rootScope)
{
var deferred = $q.defer();
require(dependencies, function()
{
$rootScope.$apply(function()
{
deferred.resolve();
});
});
return deferred.promise;
}]
}
return definition;
} });
My github repo with the entire sample is here
Pl help.

Yes, I have found the solution.
Here is my blog post for the same.
GitHub code is here : sample
It is done through mocking the module creation.
Edit :
With code. The trick here is to initialize AngularJS like this :
define(function(){ var app = angular.module('AngularAppModule', 'ngResource']);app.lazy = app; return app;});
This will add app.lazy reference in Angular, and then RequireJS can initialize the controllers the same way. Test-main.js file is the same. For running Karma, test-main will load all the scripts upfront and Require will not be intrusive. Let me know if you have further questions.

Related

AngularJS promises behavior

I have some code using angularJS.
In my application initialization logic, I keep geting an error, but I can't figure out why.
I think this may be a problem with the promise.
Here is my controller:
// TeacherCtrl.js
(function () {
"use strict";
angular
.module("app.controllers")
.controller("TeacherController", TeacherController)
.config(["$routeProvider", config]);
TeacherController.$inject = ["DataService"];
function TeacherController(DataService) {
var vm = this;
vm.data = {};
begin(7, 163000001);
function begin(appId, schoolId) {
DataService.GetData(appId, schoolId).then(function (data) {
console.log(data);
});
}
}
function config($routeProvider) {
$routeProvider
.when("/teacher", {
"templateUrl": "components/teacher/teacher.html"
, "controller": "TeacherController"
, "controllerAs": "vm"
});
}
})();
And here is my service:
// app.services.js
(function () {
"use strict";
angular
.module("app.services")
.factory("DataService", ApplicationData);
DataService.$inject = ["KeysResource"];
function DataService(KeysResource) {
return {
"GetData": GetData
};
function GetData(appId, schoolId) {
return KeysResource.load({
"appId": appId,
"schoolId": schoolId
});
}
}
})();
and this is the error I am getting:
TypeError: undefined is not a function
at iniciar (http://my.url/TeacherCtrl.js:18:72)
at new TeacherController (http://my.url/TeacherCtrl.js:15:9)
at .. // angular js code here
What it's looks like is that the ".then" function for the promise is not immediately available. Shouldn't it be???
EDIT
Here is the KeysResource I've mentioned
"use strict";
(function () {
var restClient = angular.module("restClient", ["ngResource"]);
var serviceURL;
restClient
.factory("KeysResource", ["$resource", function ($resource)
{
serviceURL = "http://my.url/service/";
return $resource(serviceURL, null, {
"load": {
"method": "get",
"url": serviceURL + "load"
}
});
}]);
})();
I've found this question with a similar problem.
The solution is related to $resource, which does not return a promise directly. If I want to use the promise of $resource, I will need to use $promise too, like that:
//TeacherCtrl.js
function begin(appId, schoolId) {
DataService.GetData(appId, schoolId).$promise.then(function (data) {
console.log(data);
});
}

How to mock a provider

My Angular 1.3 application is using the angular-translate library. In my Karma tests I'm attempting to mock the $translate provider with a Mock object I have created.
The mock object is called MockTranslate and it belongs to the myMocks module. I'm not including the source for MockTranslate in the question as it's not relevant to the question.
The subject of my test is a controller and I can quite easily mock $translate using the following:
module('myMocks');
inject(function($controller, MockTranslate) {
$controller("MyController", {
$translate: MockTranslate.create(translations);
});
});
The above mocking works, however my preference would be to mock the provider using the angular.mock.module with something like:
module('myMocks');
module("myModule", function($provide) {
$provide.provider("$translate", function(MockTranslate) {
return MockTranslate.create(translations);
});
});
But I get the following error when I run my tests:
Error: [$injector:modulerr] Failed to instantiate module function ($provide) due to: Error: [$injector:unpr] Unknown provider: MockTranslate
How do I mock a provider using angular.mock.module?
If I understood the task correctly then here is a working example:
angular.module('translateApp', [])
.controller('translateCtrl', function ($scope, $translate) {
$scope.translate = function(message) {
return $translate.translate(message);
};
})
.provider({
$translate: function() {
this.$get = function () {
return {
translate: function (msg) {
return 'OriginalTranslate: ' + msg;
}
};
};
}
});
describe('Translate Controller Test', function() {
var mockScope;
var mockTranslate;
beforeEach(module('translateApp', function($provide) {
$provide.provider('MockTranslate', function() {
this.$get = function () {
return {
translate: function (msg) {
return 'MockTranslate: ' + msg;
}
};
}
});
$provide.provider('$translate', function() {
this.$get = function (MockTranslate) {
return {
translate: function (msg) {
return MockTranslate.translate(msg);
}
};
}
});
}));
beforeEach(inject(function($controller, $rootScope, $translate) {
mockScope = $rootScope.$new();
mockTranslate = $translate;
$controller('translateCtrl', {
$scope: mockScope,
$translate: mockTranslate
});
}));
it('Translates messages', function () {
expect(mockScope.translate('cool message')).toEqual('MockTranslate: cool message');
});
});

Testing Angular controller with Require and Typescript

I am trying to create a basic test to verify that I can create a controller or service.
My app is is the following directory
app/js/app.js
My controllers are in the following directory
app/js/controllers/
Here is my karma.conf.js file
files: [
'Scripts/jquery-2.1.1.min.js',
'Scripts/require.js',
'Scripts/angular.js',
'Scripts/angular-mocks.js',
{ pattern: 'app/js/*.js', included: false },
{ pattern: 'app/js/**/*.js', included: false },
{ pattern: 'app/js/**/**/*.js', included: false },
{ pattern: 'app/js/**/**/**/*.js', included: false },
{ pattern: 'test/specs/**/*.js', included: false },
'test/test-main.js',
],
// list of files to exclude
exclude: [
'app/js/main.js'
],
test-main.js
var testFiles = [];
for (var file in window.__karma__.files) {
console.log(file);
if (/Spec\.js$/.test(file)) {
testFiles.push(file);
}
}
requirejs.config({
paths: {
// External libraries
'jquery': '../../Scripts/jquery-2.1.1.min',
'angular': '../../Scripts/angular',
'angular-mocks': '../../Scripts/angular-mocks',
'ngRoute': '../../Scripts/angular-route.min',
'angular-animate': '../../Scripts/angular-animate.min',
'angular-cookies': '../../Scripts/angular-cookies.min',
},
baseUrl: '/base/app/js',
shim: {
'angular': {
exports: 'angular',
deps: ['jquery']
},
'angular-mocks': { exports: 'angular-mocks', deps: ['angular'] },
'angular-animate': { exports: 'angular-animate', deps: ['angular'] },
'ngRoute': { exports: 'ngRoute', deps: ['angular'] },
'angular-cookies': { exports: 'angular-cookies', deps: ['angular'] },
},
// dynamically load all test files
deps: testFiles,
// we have to kickoff jasmine, as it is asynchronous
callback: window.__karma__.start
});
I added this line...
console.log(file)
To make sure the file was loaded into
window.__karma__.files
and it is.
The test lives in test/specs/
define(['angular', 'angular-mocks'], (angular, ngMock: ng.IMockStatic) => {
var module = ngMock.module;
var inject: (...fns: Function[]) => any = ngMock.inject;
describe("Create an Application", function () {
var scope, q, routeParams;
var location;
var app;
beforeEach(function () {
app = angular.module('App', []);
inject(function ($rootScope, $q: ng.IQService, $location: ng.ILocationService) {
scope = $rootScope;
q = $q;
routeParams = {};
location = $location;
});
});
it('Test Application Created', function () {
expect(true).toBe(true);
});
});
});
My app file looks like this....
import angular = require('angular');
import angularRoute = require('angular-route');
import angularAnimate = require('angular-animate');
import ds = require('services/DataService');
var app = angular.module('app', [
'ngRoute',
'ngAnimate',
'ngCookies',
'kendo.directives',
'breeze.angular',
'ui.bootstrap'
]);
export = app;
the error that i get when I try to run the test is
failed to instantiate module app due to: Module 'app' is not available! You either misspelled the module name or forgot to load it.
I am assuming it is not loading but not sure how I can tell. Is this the best way to accomplish my testing?
Any help is greatly appreciated!
Thanks so much!
If you are using Karma with Jasmine, you may be having the problem of Jasmine being too eager.
This can be fixed by making two changes, originally described in an article I wrote when I was doing a similar thing, Combining TypeScript, Jasmine and RequireJS.
In boot.js you need to comment out the env.execute(); method call:
window.onload = function() {
if (currentWindowOnload) {
currentWindowOnload();
}
htmlReporter.initialize();
//env.execute();
};
This is because the window.onload event can happen before require.js has loaded everything.
Once you have loaded the modules you need, you can then call:
// Hai Jasmine - ready to go!
jasmine.getEnv().execute();
This is what it gets compiled with your code and the -m amd switch
describe("Create an Application", function () {
var scope, q, routeParams;
var controller, location;
var route;
beforeEach(function () {
module('app', ['ngRoute', 'ngAnimate', 'ngCookies', 'kendo.directives', 'breeze.angular', 'ui.bootstrap']);
inject(function ($rootScope, $q, $location) {
scope = $rootScope;
q = $q;
routeParams = {};
location = $location;
});
// controller = new controller.ApplicationService(q, scope, route, location, _datasvc, _searchsvc);
});
it('Test Application Created', function () {
expect(true).toBe(true);
});
});
is not a module, and its synchronous
But. in a module works
define(['angular','angular-mocks'],(angular,ngMock:ng.IMockStatic)=>{
var module = ngMock.module;
var inject: (...fns: Function[])=>any = ngMock.inject;
describe("Create an Application", function () {
var scope, q, routeParams;
var location;
var app;
beforeEach(function () {
app = angular.module('App', []);
inject(function ($rootScope, $q: ng.IQService, $location: ng.ILocationService) {
scope = $rootScope;
q = $q;
routeParams = {};
location = $location;
});
});
it('Test Application Created', function () {
expect(true).toBe(true);
});
});
});
Or alternatively in a more TypeScripty w/ES6esque way :
import ngMock = require('angular-mocks');
import angular = require('angular');
var module = ngMock.module;
var inject: (...fns: Function[])=>any = ngMock.inject;
describe("Create an Application", function () {
var scope, q, routeParams;
var location;
beforeEach(function () {
var app = angular.module('app', []);
inject(function ($rootScope, $q: ng.IQService, $location: ng.ILocationService) {
scope = $rootScope;
q = $q;
routeParams = {};
location = $location;
});
});
it('Test Application Created', function () {
expect(true).toBe(true);
});
});
check that you are loading angular as in a script tag
'files:[Scripts/angular.js',... ,
that's why you get
WARNING:tried load angular more than once
Put it in a { pattern: 'Scripts/angular.js', included: false ...],
you need to work out the requirejs.confg until the deps chain are satisfied. in order., and thats one of the reasons why you set the shim config with
'angular-mocks': { exports: 'angular-mocks', deps: ['angular'] },
so when requiring angular-mocks angular is required too ,
have a look at [this github repo](https://github.com/tnajdek/angular-requirejs-
seed),
...And once you have an instance attached to angular.module /mock.module in your spec , check Steve's answer again ,
If you can't find boot.js , you might be using jasmine bundled with the Karma adapter. in that case it should work given you have sorted out the require test-maiin.js / config / "NG_DEFER_BOOTSTRAP!" /resumeBootstrap story

angular mean.io developer prodution uglify error on minification

Can anyone explain me what's wrong with this code:
.controller('ArticleCreateCtrl', ['$scope', '$state', '$filter', 'Articles', function ($scope, $state, $filter, Articles) {
$scope.article = {};
$scope.save = function(){
$scope.article.categories = $filter('strcstoarray')($scope.article.categories);
Articles.store($scope.article).then(
function(data) {
$scope.article = data;
return $state.transitionTo('articles');
},
function(err) {
throw new Error(err);
}
);
};
}])
In the local machine works well when I run it
in heroku (prodution enviroment therefore with all the js minify)
I get :
Error: assignment to undeclared variable data
UPDATE (My service)
angular.module('mean.system')
.factory('Base',['Restangular', function(Restangular) {
return function(route){
var elements = Restangular.all(route);
return {
one : function (id) {
return Restangular.one(route, id).get();
},
all : function () {
return elements.getList();
},
store : function(data) {
return elements.post(data);
},
copy : function(original) {
return Restangular.copy(original);
},
getElements : function() {
return elements;
}
};
};
}]);
//Articles service used for articles REST endpoint
angular.module('mean.articles').factory('Articles', ['Base', function(Base) {
_.mixin({
'findByCategory': function(collection,category) {
return _.filter(collection, function(item) {
return _.contains(item.categories, category);
});
}
});
function Articles() {
this.findByCategory = function(collection,category){
return _.findByCategory(collection,category);
};
}
return angular.extend(Base('articles'), new Articles());
}]);
Make sure you have uglify configured properly in Gruntfile.js, with mangle: false
uglify: {
options: {
mangle: false
},
production: {
files: '<%= assets.js %>'
}
},
I had the same problem, it's because you must use the ngmin task, which prepares some angular libraries to be minified.
At package.json add the following line before uglify:
"grunt-ngmin": "0.0.3"
Then update dependencies:
npm install
Then at the Gruntfile.js added the ngmin task:
ngmin: {
production: {
files: '<%= assets.js %>'
}
},
Remember to add the ngmin task BEFORE uglify:
grunt.registerTask('default', ['clean','cssmin', 'ngmin','uglify', 'concurrent']);
The next time you'll run the server in production mode your code will work.

Controller and services are not loading in angularjs

I have integrated requirejs with my angular app.
But while loading app, it gives me an error 'Argument 'appCtrl' is not a function, got undefined'
Here is my controller code :
define(['Angular'], function (angular) {
function appCtrl($scope, pathServices) {
alert('sa');
}
function homeCtrl($scope, brandService) {
console.log('dfd');
}
});
And along with this, it gives error for 'unknown provider pathServices'
Service code is :
serviceConfig.js
define([
'Angular',
'common/Services/services',
'current/js/services'
], function(angular, commonServices, loacalStorageServices, currentServices) {
"use strict";
var services = {
commonServices : commonServices,
currentServices : currentServices,
};
var initialize = function (angModule) {
angular.forEach(services,function(service, name) {
angModule.service(name, service);
});
}
return {
initialize: initialize
};
});
common/services.js
define(['Angular'], function (angular) {
var app = angular.module('myApp.services', []);
app.factory('pathServices', function($http, $q, $rootScope) {
function pathServices() {
alert('as');
}
return new pathServices();
});
app.factory('anotherServices', function($http, $q, $rootScope) {
function anotherServices() {
alert('as');
}
return new anotherServices();
});
});
current/services.js
define(['Angular'], function(angular) {
var app = angular.module('myApp.services', []);
app.factory('brandsService', function() {
function brandsService() {
var autoCompleteData = [];
this.getSource = function() {
return autoCompleteData;
}
this.setSource = function(states) {
autoCompleteData = states;
}
}
return new brandsService();
});
});
in serviceConfig.js I have included 2 service files.. But the problem is, the last current/service.js file overwrites all files.. How can I include multiple service files ?
I am new to requirejs. How can I use controller function and services using requirejs ?
Can anyone help ?
You have to declare your functions in the global (window) namespace, or register them in your module with the moduleName.controller('controllerName',controllerFn)
So either
define(['Angular'], function (angular) {
window.appCtrl = function($scope, pathServices) {
alert('sa');
}
window.homeCtrl = function($scope, brandService) {
console.log('dfd');
}
});
or
define(['Angular'], function (angular) {
var module = angular.module('theModuleName');
module.controller('appCtrl', function($scope, pathServices) {
alert('sa');
});
module.controller('homeCtrl', function($scope, brandService) {
console.log('dfd');
}
});
should fix this error (I prefer the second approach).

Resources