Dealing With Dependencies in angularjs Testing - javascript

This is for work (I have permission) so I can't post exact code.
So I have to test controllers of a large module. The module has a large config function with a bunch of controllers for the logic of the different pages.
For the actual application it's loaded with bower, which is irritating since I'm testing with Karma-Browserify and npm. So the the dependencies are a mess. I basically have to import everything that was loaded from bower.json to package.json.
This is my karma.conf.js:
module.exports = function(config) {
config.set({
basePath: 'resources',
browserify: {
debug: true,
transform: [ 'browserify-shim' ]
},
browsers: [ 'PhantomJS' ],
captureTimeout: 60000,
client: {
mocha: {}
},
files: [
'tests/assist/test.js',
'assets/scripts/libs/logger.min.js'
],
frameworks: [ 'browserify', 'phantomjs-shim', 'mocha', 'chai' ],
port: 8080,
preprocessors: {
'tests/assist/controller.js': [ 'browserify' ]
},
reporters: [ 'mocha', 'coverage' ],
singleRun: true
});
};
So the code below this is my test.js (removing some company-specific names). Also I need to put angular.mock. or it won't work
require('angular');
require('angular-mocks');
//Main module needs these
jQuery = require('jquery');
require('angular-bootstrap');
require('angular-recaptcha');
require('angular-ui-router');
require('ngstorage');
require(**The path to the main module**);
require(**The path to a service it uses**);
require(**The path to a service it uses**);
require(**The path to a service it uses**);
describe('Blah', function () {
beforeEach(angular.mock.module('myApp'));
var $controller;
beforeEach(angular.mock.inject(function(_$controller_) {
$controller = _$controller_;
}));
describe('blahblah', function () {
it('sets loading to true', function () {
var $scope = {};
var controller = $controller('controller', {$scope: $scope});
assert($scope.showLoading === true);
});
});
});
The main module:
(function() {
'use strict';
})();
// Jquery noconflict
jQuery.noConflict();
var myApp = angular.module('myApp', ['ui.router', 'ngStorage', 'vcRecaptcha', 'ui.bootstrap']);
myApp.config(['$stateProvider', '$urlRouterProvider', '$locationProvider', function($stateProvider, $urlRouterProvider, $locationProvider) {
...
}])
.run([blah bunch of dependencies]) {
...
}]);
The controller (separate fie):
'use strict';
myApp.controller('controller', ['$scope', '$http', '$localStorage', 'service1', 'service2', 'service3',
function ($scope, $http, $localStorage, service1, service2, service3) {
..
}
...
As you can see I'm in dependency hell. I got the example test on the angular site to work, the main problem is with the dependencies and myApp not being visible to the controller. "ReferenceError: Can't find variable: myApp" in controllers/services
If anyone has a better way of going about testing I'm all ears.

This is not about dependency hell, not about testing also.
The code seems to rely on myApp global variable, this is strictly opposite to what Angular modules are for.
myApp should be a local variable that is defined dynamically in each function scope
(function () {
var myApp = angular.module('myApp', [...]);
...
})();
(function () {
var myApp = angular.module('myApp');
myApp.controller('controller', ...)
...
})();

Related

How to resolve dependencies for a project using AngularJS and RequireJS?

I have a simple AngularJS application which I am trying to refactor to use RequireJS.
Since controllers and services are loaded async, I can't use ng-app in my index.html.
Following is my main.js
require.config({
paths: {
"angular": '../../bower_components/angular/angular',
"angularCookies": '../../bower_components/angular-cookies/angular-cookies'
},
shim: {
angular: {
exports: "angular"
},
angularCookies : {
deps: ["angular"]
}
}
});
require(['angular', './login/js/login'],
function (angular) {
angular.bootstrap(document, ['loginApp']);
}
);
My login.js is where I am defining an angular module.
Following is my login.js
'use strict';
define(['angular',
'angularCookies',
'./login.controller'],
function(angular, angularCookies, loginController) {
angular.module('loginApp', [
'ngCookies'])
.config(['$cookiesProvider',
function($cookiesProvider) {
$cookiesProvider.defaults.path = '/';
$cookiesProvider.defaults.domain = 'localhost';
}
])
.run(['$cookies',
'loginService',
function($cookies, loginService) {
}
]).controller(loginController);
});
As seen, it is dependent on loginController and loginController is dependent on loginService.
My loginService is defined as --
"use strict";
define(['angular',
'angularCookies'],
function (angular, angularCookies) {
var loginService = angular.module('loginApp')
.factory('loginService', [
'$http',
'$cookies',
function ($http, $cookies) {
// My functions and other code here.
}]);
return loginService;
});
With this configuration I am getting an error -
Module 'loginApp' is not available! You either misspelled the module name or forgot to load it. If registering a module ensure that you specify the dependencies as the second argument.
What am I missing here?
What configuration do I need to do to make it right?
I see a couple of problems. First the app shouldn't be created inside login. The app is the base of all controllers and services.
So I would move the app creation to another file called app.js.
Then in my require config:
shim: {
'app': {
deps: ['angular', 'angular-route', 'angularCookies']
},
angularCookies : {
deps: ["angular"]
}
}
And:
require
(
[
'app'
],
function(app)
{
angular.bootstrap(document, ['loginApp']);
}
);
And then your controller would be:
define(['loginApp'], function(app)
{
app.controller('loginController',
[
'$scope',
function($scope)
{
//...
}
]);
});

AngularJS controller does not run on RequireJS config

I am trying to configure an web app with requireJS and angularJS. I come from marionette configuration and I am trying to have a similar one in angular (in concepts like views and controllers) first so I want to be able to map #/test to my controller and log in the console one message.
I've seen Does AngularJS support AMD like RequireJS? and RequireJS and AngularJS and I kind of got the differences and from my point of view my config should work... but it does not...
Here is my code:
File: app.config.js
require.config({
shim: {
angular: {
exports: 'angular'
},
angularRoute: ['angular']
},
paths: {
angular: '../lib/angular',
angularRoute: '../lib/angular-route'
}
});
require(['angular', 'app', 'routes/index'], function (angular) {
angular.bootstrap(document, ['app']);
});
File: app.js
define(['angular', 'angularRoute'], function (angular) {
//angular.module('app.controllers', []);
var app = angular.module('app', ['ngRoute']);
return app;
});
File: routes/index.js
define(['angular', 'app', 'controllers/index'], function (angular, app) {
app.config(['$routeProvider', function ($routeProvider) {
$routeProvider.when('/', { templateUrl: require.toUrl('/resources/js/app/templates/test.html'), controller: 'indexController'});
}]);
});
File: controllers/index.js
define(['angular', 'app'], function (angular, app) {
//var appControllers = angular.module('app.controllers');
app.controller('indexController', ['$scope', function ($scope) {
console.log('cascade...');
}]);
});
What am i missing? When i access #/test, i don't see "cascade" in the console, should I?.. Right?
Thanks in advance.

Adding third party vendor dependencies to mean.js

I've been struggling trying to hook up this dependency (http://flowjs.github.io/ng-flow/) to my mean.js application. I'm thinking it's simply a naming problem.
The error I'm getting is: Error: [$injector:unpr] Unknown provider: flowProvider <- flow
I've tried 'flow', 'Flow', 'ngFlow', etc. Any help would be greatly appreciated.
modules/core/client/app/config.js
'use strict';
// Init the application configuration module for AngularJS application
var ApplicationConfiguration = (function() {
// Init module configuration options
var applicationModuleName = 'mean';
var applicationModuleVendorDependencies = ['ngResource', 'ngAnimate', 'ui.router', 'ui.bootstrap', 'ui.utils', 'angularFileUpload', 'flow'];
// Add a new vertical module
var registerModule = function(moduleName, dependencies) {
// Create angular module
angular.module(moduleName, dependencies || []);
// Add the module to the AngularJS configuration file
angular.module(applicationModuleName).requires.push(moduleName);
};
return {
applicationModuleName: applicationModuleName,
applicationModuleVendorDependencies: applicationModuleVendorDependencies,
registerModule: registerModule
};
})();
modules/properties/client/properties.client.controller.js:
'use strict';
var $ = $ || {};
// Properties controller
angular.module('properties').controller('PropertiesController', [
'$scope',
'$stateParams',
'$location',
'Authentication',
'Brands',
'Applications',
'Properties',
'flow',
function($scope, $stateParams, $location, Authentication, Brands, Applications, Properties, flow) {
.......
modules/properties/client/properties.client.module.js:
'use strict';
// Use applicaion configuration module to register a new module
//ApplicationConfiguration.registerModule('properties');
ApplicationConfiguration.registerModule('properties',['flow']);
config/assets/default.js:
'use strict';
module.exports = {
client: {
lib: {
css: [
'public/lib/bootstrap/dist/css/bootstrap.css',
'public/lib/bootstrap/dist/css/bootstrap-theme.css'
],
js: [
'public/lib/angular/angular.js',
'public/lib/angular-resource/angular-resource.js',
'public/lib/angular-animate/angular-animate.js',
'public/lib/angular-ui-router/release/angular-ui-router.js',
'public/lib/angular-ui-utils/ui-utils.js',
'public/lib/angular-bootstrap/ui-bootstrap-tpls.js',
'public/lib/angular-file-upload/angular-file-upload.js',
'public/lib/jquery/dist/jquery.js',
'public/lib/ng-flow/dist/ng-flow-standalone.js'
],
tests: ['public/lib/angular-mocks/angular-mocks.js']
},
css: [
'modules/*/client/css/*.css'
],
less: [
'modules/*/client/less/*.less'
],
sass: [
'modules/*/client/scss/*.scss'
],
js: [
'modules/core/client/app/config.js',
'modules/core/client/app/init.js',
'modules/*/client/*.js',
'modules/*/client/**/*.js'
],
views: ['modules/*/client/views/**/*.html']
},
server: {
allJS: ['gruntfile.js', 'server.js', 'config/**/*.js', 'modules/*/server/**/*.js'],
models: 'modules/*/server/models/**/*.js',
routes: ['modules/*[!core]/server/routes/**/*.js', 'modules/core/server/routes/**/*.js'],
sockets: 'modules/*/server/sockets/**/*.js',
config: 'modules/*/server/config/*.js',
policies: 'modules/*/server/policies/*.js',
views: 'modules/*/server/views/*.html'
}
};
So long as you loaded the js file correctly, you can use flowFactory to create a flow instance. Then create a flow object and use that to refer to flow.
angular.module('properties').controller('PropertiesController', [
'$scope',
'$stateParams',
'$location',
'Authentication',
'Brands',
'Applications',
'Properties',
'flowFactory',
function($scope, $stateParams, $location, Authentication, Brands, Applications, Properties, flowFactory) {
$scope.existingFlowObject = flowFactory.create({
target: 'http://example.com/upload'
});
................................................................
So try changing flow to flowFactory and see if that leads to any provider dependency issues.
I don't see an answer to this. Changing the registerModule part.
ApplicationConfiguration.registerModule('flow');
to .client.module.js
This is mean.js version 0.4.0
you don't have to include it into your project dependency
I modified it in this way :
var applicationModuleVendorDependencies = ['ngResource', 'ngAnimate', 'ui.router', 'ui.bootstrap', 'ui.utils', 'angularFileUpload'];

Angular + Requirejs - how to return multiple modules?

How can I return multiple angular modules in requirejs environment?
this is my app.js,
define([
'angular',
'angular-route',
'jquery'
], function (ng,ngRoute,$) {
'use strict';
console.log($('h1').length);
return ng.module('myApp', ['ngRoute']);
});
And I need a few more modules to return,
ng.module('myAppModule1', ['ngRoute']);
ng.module('myAppModule2', ['ngRoute']);
ng.module('myAppModule3', ['ngRoute']);
a controller example, for instance I want to get 'myAppModule3' in app.js,
define(['app'], function (app) {
var myAppModule = angular.module('myAppModule3');
myAppModule.controller('welcomeController', ['$scope', function($scope) {
//your minsafe controller
$scope.message = "Message from WelcomeController";
}]);
});
You could change app.js to return an object whose fields are the modules:
define([
'angular',
'angular-route',
'jquery'
], function (ng,ngRoute,$) {
'use strict';
console.log($('h1').length);
return {
myApp: ng.module('myApp', ['ngRoute']),
myAppModule1: ng.module('myAppModule1', ['ngRoute']),
myAppModule2: ng.module('myAppModule2', ['ngRoute']),
myAppModule3: ng.module('myAppModule3', ['ngRoute'])
};
});
And change your controller like this:
define(['app'], function (app) {
app.myAppModule3.controller('welcomeController', ['$scope', function($scope) {
//your minsafe controller
$scope.message = "Message from WelcomeController";
}]);
});
The generic (non-Angular specific) way is to use an object:
return {module1: /*..*/, module2: /*...*/ };
Then you just access to the values:
define(['app'], function (app) {
var module1 = app.module1;
});
However in Angular you just registered 'myAppModule1' in the Angular global. There is no need to do the object return, you can retrieve the registered module using the angular object:
define(['angular'], function (angular) {
var module1 = angular.module('myAppModule1');
// without extra parameter it tells angular to retrive an existing
// module
});
Update: I just realize that you did it in your code. It didn't worked? Maybe be you have a dependency issue, make sure that app.js is loaded first.

TypeError: 'undefined' is not a function (evaluating 'angular.element(window).width()')

Hi I have a pretty complex app that I have been writing Karma unit tests for. I have a lot of tests that were written and running sucessfully but then I changed a few things in the app and now I am getting tons of errors. Been staring at it for hours and can't figure it out.
Error I get:
TypeError: 'undefined' is not a function (evaluating 'angular.element(window).width()')
at /Users/Desktop/app/scripts/app.js:9
at invoke (/Users/Desktop/app/bower_components/angular/angular.js:3869)
at /Users/Desktop/app/bower_components/angular/angular.js:3715
My config file looks like this
// Karma configuration
// http://karma-runner.github.io/0.10/config/configuration-file.html
module.exports = function(config) {
config.set({
// base path, that will be used to resolve files and exclude
basePath: '',
// testing framework to use (jasmine/mocha/qunit/...)
frameworks: ['jasmine'],
// list of files / patterns to load in the browser
files: [
'app/bower_components/angular/angular.js',
'app/bower_components/angular-mocks/angular-mocks.js',
'app/bower_components/angular-resource/angular-resource.js',
'app/bower_components/angular-cookies/angular-cookies.js',
'app/bower_components/angular-sanitize/angular-sanitize.js',
'app/bower_components/angular-route/angular-route.js',
'app/bower_components/angular-bootstrap/*.js',
'app/bower_components/angular-ui-date/src/date.js',
'app/bower_components/angular-ui-sortable/*.js',
'app/bower_components/angular-ui-router/src/*.js',
'app/bower_components/d3/*.js',
'app/bower_components/nvd3/*.js',
'app/bower_components/angularjs-nvd3-directives/src/directives/*.js',
'app/bower_components/jquery/dist/*.js',
'app/bower_components/ng-grid/*.js',
'app/bower_components/ng-grid/build/*.js',
'app/bower_components/ng-grid/plugins/*.js',
'app/scripts/app.js',
'app/bower_components/angular',
'app/scripts/*.js',
'app/scripts/**/*.js',
'app/scripts/***/**/*.js',
'test/client/spec/**/*.js'
],
// list of files / patterns to exclude
exclude: [
'app/bower_components/*/angular-scenario.js',
'app/bower_components/angular-ui-router/src/compat.js',
'app/bower_components/angularjs-nvd3-directives/src/directives/intro.js',
'app/bower_components/angularjs-nvd3-directives/src/directives/outro.js'
],
// web server port
port: 8080,
// level of logging
logLevel: config.LOG_INFO,
// enable / disable watching file and executing tests whenever any file changes
autoWatch: true,
// Start this browser
browsers: ['PhantomJS'],
//Coverage Options
preprocessors: {
'app/scripts/**.js': 'coverage'
},
reporters: ['dots', 'coverage']
});
};
The controller I'm testing:
angular.module("vizApp").controller("BasicSearchCtrl", function ($rootScope, $scope, SearchService) {
"use strict";
$scope.searchContent = null;
$scope.$watch("searchContent", function (newVal, oldVal, scope) {
SearchService.setBasicSearch($scope.searchContent);
});
});
And the test I'm writing is:
describe("Controller: BasicSearchCtrl", function () {
"use strict";
var scope, BasicSearchController, httpBackend, searchSerivce;
//load the controller"s module
beforeEach(module("vizApp"));
beforeEach(function () {
angular.mock.inject(function ($injector) {
httpBackend = $injector.get("$httpBackend");
});
});
//initialize the controller and a mock scope
beforeEach(inject(function ($controller, $rootScope, $injector) {
// create a new child scope
scope = $rootScope.$new();
scope.SearchService = $injector.get("SearchService");
//create a new instance of basic search controller
BasicSearchController = $controller("BasicSearchCtrl", { $scope: scope });
}));
//check the initial state of the search content
it("very basic search", function () {
expect(scope.searchContent).toBeUndefined;
expect(scope.SearchService.basicSearch).toBeUndefined;
});
But I keep getting this cryptic error...about line 9 on app.js but app.js looks like this so I dont get it.
angular.module('vizApp', [
'ngCookies',
'ngResource',
'ngSanitize',
'ui.bootstrap',
'ui.router',
'nvd3ChartDirectives',
'ngGrid',
'ui.sortable',
'ui.date'
]).config(function ($stateProvider, $urlRouterProvider, $locationProvider, $httpProvider) {
'use strict';
// For any unmatched url, redirect to /
$urlRouterProvider.otherwise('/');
width() is not a supported by angular, which internally relies on jqLite https://docs.angularjs.org/api/ng/function/angular.element.
You would have to either include jquery and use jquery's width() function or better still inject Angular's $window service and use $window[0].innerWidth or $window[0].offsetWidth or $window[0].clientWidth.
If you really want to use jquery instead of jqlite, then your mock config is the problem:
Just move the jquery.js to the first position in the file list. If jquery is after angular.js, you'll just get a jqlite object which doesn't have viewport methods.

Categories