Angular.module minification bug - javascript

Having the darnedest time trying to figure out why minification is not working.
I have injected via an array object my providers prior the function per numerous suggestions across the web and yet still "Unknown provider: aProvider <- a"
Regular:
var app = angular.module('bpwApp', ['ui.bootstrap', 'ui', 'myTabs'])
.config(['$routeProvider', '$locationProvider', function($routeProvider, $locationProvider){
$routeProvider.
when('/', {templateUrl: 'partials/home.jade', controller: HomeCtrl});
$locationProvider.html5Mode(true);
}])
Minified:
var app = angular.module('bpwApp', ['ui.bootstrap', 'ui', 'myTabs'])
.config(['$routeProvider', '$locationProvider', function(a, b){
a.
when('/', {templateUrl: 'partials/home.jade', controller: HomeCtrl});
b.html5Mode(true);
}])
Any suggestion would be much obliged!

I ran into this problem before with Grunt.js Uglify plugin.
One of the options are mangle
uglify: {
options: {
mangle: false
},
Which I believe runs regex functions on "like strings" and minifys them.
For example:
angular.module("imgur", ["imgur.global","imgur.album"]);
Would become:
angular.module("a", ["a.global","a.album"]);
Disable it --- this feature doesn't play nice with Angular.
Edit:
To be more precise as #JoshDavidMiller explains:
Uglify mangle only mangles like variables, which is what actually causes the AngularJS problem. That is, the problem is in injection and not definition.
function MyCtrl($scope, myService) would get mangled to function MyCtrl(a, b), but the service definition inside of a string should never get altered.
Running ng-min before running uglify solves this problem.

Problem
From AngularJS: The Bad Parts:
Angular has a built in dependency injector that will pass appropriate
objects to your function based on the names of its parameters:
function MyController($scope, $window) {
// ...
}
Here, the names of the parameters $scope and $window will be
matched against a list of known names, and corresponding objects get
instantiated and passed to the function. Angular gets the parameter
names by calling toString() on the function, and then parsing the
function definition.
The problem with this, of course, is that it stops working the
moment you minify your code. Since you care about user experience
you will be minifying your code, thus using this DI mechanism will
break your app. In fact, a common development methodology is to use
unminified code in development to ease debugging, and then to minify
the code when pushing to production or staging. In that case, this
problem won’t rear its ugly head until you’re at the point where it
hurts the most.
(...)
Since this dependency injection mechanism doesn’t actually work in the
general case, Angular also provides a mechanism that does. To be sure,
it provides two. You can either pass along an array like so:
module.controller('MyController', ['$scope', '$window', MyController]);
Or you can set the $inject property on your constructor:
MyController.$inject = ['$scope', '$window'];
Solution
You can use ng-annotate for auto adding annotations required for minifying:
ng-annotate adds and removes AngularJS dependency injection
annotations. It is non-intrusive so your source code stays exactly the
same otherwise. No lost comments or moved lines.
ng-annotate is faster and stabler than ngmin (which is now deprecated) and it has plugins for many tools:
grunt-ng-annotate
gulp-ng-annotate
browserify-annotate
Starting from AngularJS 1.3 there's also a new param in ngApp called ngStrictDi:
if this attribute is present on the app element, the injector will be
created in "strict-di" mode. This means that the application will fail
to invoke functions which do not use explicit function annotation (and
are thus unsuitable for minification), as described in the Dependency
Injection guide, and useful debugging info will assist in tracking
down the root of these bugs.

I got same error. However, for me, the problem is directives' controller declaration. You should do this instead.
myModule.directive('directiveName', function factory(injectables) {
var directiveDefinitionObject = {
templateUrl: 'directive.html',
replace: false,
restrict: 'A',
controller: ["$scope", "$element", "$attrs", "$transclude", "otherInjectables",
function($scope, $element, $attrs, $transclude, otherInjectables) { ... }]
};
return directiveDefinitionObject;
});
https://github.com/angular/angular.js/pull/3125

I had a similar issue using grunt, ngmin and uglify.
I ran the process in this order: concat, ngmin, uglify
I was continuing to get the $injector error from angular until I added in the uglify options mangle: false - then everything was fixed.
I also tried to add the exceptions to uglify like this:
options: {
mangle: {
except: ['jQuery', 'angular']
}
}
But to no avail...
Here is my gruntFile.js for further clarification:
module.exports = function(grunt) {
'use strict';
// Configuration goes here
grunt.initConfig({
pkg: require('./package.json'),
watch: {
files: ['scripts/**/*.js', 'test/**/*spec.js', 'GruntFile.js'],
tasks: ['test', 'ngmin']
},
jasmine : {
// Your project's source files
src : ['bower_components/angular/angular.js', 'bower_components/angular-mocks/angular-mocks.js', 'scripts/app.js', 'scripts/**/*.js' ],
// Your Jasmine spec files
options : {
specs : 'test/**/*spec.js',
helpers: 'test/lib/*.js'
}
},
concat: {
dist : {
src: ['scripts/app.js', 'scripts/**/*.js'],
dest: 'production/js/concat.js'
}
},
ngmin: {
angular: {
src : ['production/js/concat.js'],
dest : 'production/js/ngmin.js'
}
},
uglify : {
options: {
report: 'min',
mangle: false
},
my_target : {
files : {
'production/app/app.min.js' : ['production/js/ngmin.js']
}
}
},
docular : {
groups: [],
showDocularDocs: false,
showAngularDocs: false
}
});
// Load plugins here
grunt.loadNpmTasks('grunt-ngmin');
grunt.loadNpmTasks('grunt-docular');
grunt.loadNpmTasks('grunt-contrib-uglify');
grunt.loadNpmTasks('grunt-contrib-jasmine');
grunt.loadNpmTasks('grunt-contrib-watch');
grunt.loadNpmTasks('grunt-contrib-concat');
grunt.loadNpmTasks('grunt-contrib-connect');
// Define your tasks here
grunt.registerTask('test', ['jasmine']);
grunt.registerTask('build', ['concat', 'ngmin', 'uglify']);
grunt.registerTask('default', ['test', 'build', 'watch']);
};

AndrewM96 suggestion of ng-min is right.
The alignment and white space matters to Uglify as well as Angular.

I had a similar problem. And solved it the following way. We need to run a Gulp module called gulp-ng-annotate before we run uglify.
So we install that module
npm install gulp-ng-annotate --save-dev
Then do the require in Gulpfile.js
ngannotate = require(‘gulp-ng-annotate’)
And in your usemin task do something like this
js: [ngannotate(), uglify(),rev()]
That solved it for me.
[EDIT: Fixed typos]

Uglify has an option to disable mangling on specific files:
options: {
mangle: {
except: ['jQuery', 'angular']
}
}
https://github.com/gruntjs/grunt-contrib-uglify#reserved-identifiers

This is very difficult to debug because a lot of services are named the same (mostly e or a). This will not solve the error, but will provide you with the name of the unresolved service which enables you to track down, in the uglified output, the location in the code and finally enables you to solve the issue:
Go into lib/scope.jsof Uglify2 (node_modules/grunt-contrib-uglify/node_modules/uglify-js/lib/scope.js) and replace the line
this.mangled_name = this.scope.next_mangled(options);
with
this.mangled_name = this.name + "__debugging_" + counter++

Related

Error: [$injector:modulerr] Failed to instantiate module app due to: State 'frameworks'' is already defined registerState#http://localhost:3030 [duplicate]

I am developing an AngularJS application. To ship the code in production, I'm using this Grunt configuration/task:
grunt.registerTask( 'compile', [
'sass:compile', 'copy:compile_assets', 'ngAnnotate', 'concat:compile_js', 'uglify', 'index:compile'
]);
It's really hard to debug, and it's kind of a question to people who already ran into such problems and can point to some direction.
My main module is including those submodules:
angular
.module('controlcenter', [
'ui.router',
'ui.bootstrap',
'templates-app',
'templates-common',
'authentication',
'api',
'reports',
'interceptors',
'controlcenter.websites',
'controlcenter.users',
'controlcenter.campaigns',
'controlcenter.reports',
'controlcenter.login'
])
.run(run);
The error I get is following:
Uncaught Error: [$injector:modulerr] Failed to instantiate module controlcenter due to:
Error: [$injector:modulerr] Failed to instantiate module controlcenter.websites due to:
Error: State 'websites'' is already defined
If I remove the websites module, I get the same error for
controlcenter.users.
I am using the ui-router to handle routing inside the app.
After my build process (for integration testing), everything works just fine:
grunt.registerTask( 'build', [
'clean', 'html2js', 'jshint', 'sass:build',
'concat:build_css', 'copy:build_app_assets', 'copy:build_vendor_assets',
'copy:build_appjs', 'copy:build_vendorjs', 'copy:build_vendorcss', 'index:build', 'karmaconfig',
'karma:continuous'
]);
So maybe ngAnnotate or or concat/uglify are doing weird things here?
UPDATE 1:
It has something to do with my configuration of the modules. Here is the code:
angular
.module('controlcenter.websites',
[
'ui.router'
]
)
.config(config);
config.$inject = ['$stateProvider'];
function config($stateProvider) {
$stateProvider.state( 'websites', {
url: '/websites',
views: {
"main": {
controller: 'WebsitesController',
templateUrl: 'websites/websites.tpl.html'
}
}
});
}
When I change the name of the state to websites_2, I get an error
with 'websites_2 is already defined'.
When I remove the module completely, the next one hast the same problem inside the config file. So is the structure wrong?
Update 2:
The problem seems concat related.
It takes every JS file and adds it one after another to one, bigger file. All of my modules are at the end. The last module always has the problem with 'state already defined'. So it's not just the order of the modules appending to each other, it's something elsse...
Update 3:
I placed my code (I've excluded every Controller-Code and functions, just the scaffold) in a gist. This is the outcome after my compile process, without uglifying it.
Issue:
You have multiple files that contains a config function to configure your module, like this:
angular
.module('controlcenter.websites', [])
.config(config);
function config() {
// ...
}
The problem is that after you concatenate all files you end up with a big file with multiple declarations of config. Because of JavaScript's variable hoisting, all declarations are moved to the top and only the very last of them is evaluated, and this one is:
function config($stateProvider) {
$stateProvider.state( 'websites', {
url: '/websites',
views: {
"main": {
controller: 'WebsitesController',
templateUrl: 'websites/overview/websites.tpl.html'
}
},
data : {requiresLogin : true }
});
}
Hence, each time you .config(config) a module, you are telling Angular to configure your module with that particular configuration function, which means that it executes multiple times and tries to define the state websites more than once.
Solution:
Wrap each JavaScript file code with a closure. This way you will avoid declaring a variable/function more than once:
(function (angular) {
'use strict';
angular
.module('controlcenter.website.details', ['ui.router'])
.config(config);
config.$inject = ['$stateProvider'];
function config($stateProvider) {
$stateProvider
.state( 'websiteDetails', {
url: '/websiteDetails/:id',
views: {
"main": {
controller: 'WebsiteDetailsController',
templateUrl: 'websites/details/website.details.tpl.html'
}
},
data : {requiresLogin : true }
})
.state( 'websiteDetails.categories', {
url: '/categories',
views: {
"detailsContent": {
templateUrl: 'websites/details/website.details.categories.tpl.html'
}
},
data : {requiresLogin : true }
})
;
}
})(window.angular);
Edit:
I strongly recommend you wrap your files into closures. However, if you still don't want to do that, you can name your functions according to their respective modules. This way your configuration function for controlcenter.website.details would become controlcenterWebsiteDetailsConfig. Another option is to wrap your code during build phase with grunt-wrap.
window.angular and closures: This is a technique I like to use on my code when I'm going to uglify it. By wrapping your code into a closure and giving it a parameter called angular with the actual value of window.angular you are actually creating a variable that can be uglified. This code, for instance:
(function (angular) {
// You could also declare a variable, instead of a closure parameter:
// var angular = window.angular;
angular.module('app', ['controllers']);
angular.module('controllers', []);
// ...
})(window.angular);
Could be easily uglified to this (notice that every reference to angular is replaced by a):
!function(a){a.module("app",["controllers"]),a.module("controllers",[])}(window.angular);
On the other side, an unwrapped code snippet like this:
angular.module('app', ['controllers']);
angular.module('controllers', []);
Would become:
angular.module("app",["controllers"]),angular.module("controllers",[]);
For more on closures, check this post and this post.
If you check it in the concatenated file, do you have the states defined twice? Can it be that you are copying the files twice? Check the temporary folders from where you are taking the files (also in grunt config, what you are copying and what you are deleting...).
So I had the same problem but with the following setup:
yeoman angular-fullstack (using typescript)
Webstorm
With the angular-fullstack configuration, the closures were already implemented (as Danilo Valente suggests) so I struggled quite a bit until I found out that in Webstorm, I had the typescript compiler enabled which compiled all of my *.ts files to *.js. But since Webstorm is so 'smart', it does not show these compiled files in the working tree. Grunt however concatenated of course all files regardless if it is typescript of JS. That's why - in the end- all of my states were defined twice.
So the obvious fix: Disabled typescript compiler of webstorm and deleted all the generated *.js files and it works.

Angularjs - Dynamic configuration based on Environment

I have a sample angular APP - app.js
angular
.module('myUiApp', [
'restangular',
'ngRoute',
'ngCookies',
'ui.bootstrap',
'ui.sortable',
'smart-table',
'config'
])
.config(function($routeProvider, $httpProvider, $sceProvider, $logProvider, RestangularProvider, config) {
RestangularProvider.setBaseUrl(config.apiBaseUrl);
RestangularProvider.setDefaultHeaders({'Content-Type': 'application/json'});
//routing here
.....
});
my Config.js looks like -
angular.module('config', []).service('config', function($location, ENV) {
return ENV.dev;
});
my constants.js looks like -
'use strict';
angular.module('config', []).constant('ENV', (function() {
return {
dev: {
appBaseUrl:'http://localhost:9000/',
apiBaseUrl:'http://localhost:8082/api/'
}
}
})());
I am getting the error saying, Failed to instantiate module myUiApp due to:
[$injector:unpr] Unknown provider: config.
My assumption is injecting config module will invoke the service, which in turn return the json object. any thoughts or suggesstions to do this dynamic config better?
You can only inject providers into an angular .config() block. You're attempting to inject a service, and that is likely the cause of your error.
Also, you have angular.module('config', []) in two different places. This should only be used once to instantiate the module. Use angular.module('config') (without the second argument) subsequently to reference that module.
I would avoid calling the module config, in favor of something that isn't a method used by angular module.config() -- maybe myConfigModule
Secondly, make sure your script includes the constants.js file and the Config.js file before it includes the app.js file
Lastly double check that this situtation is not affecting you:
defining the module twice with angular.module('config', []) ( emphasis on the [ ] ..) When you define the module with the square brackets, you are saying "New Module". In the second file that you include, change it to angular.module('config') -- or, combine the files into this:
angular.module('myConfigModule', [])
.constant('ENV', (function() {
return {
dev: {
appBaseUrl:'http://localhost:9000/',
apiBaseUrl:'http://localhost:8082/api/'
}
}
}).service('config', function($location, ENV) {
return ENV.dev;
});
UPDATE: And typically I see this syntax for controllers, services, anything that is injecting anything else
.service('config', ['$location', 'ENV', function($location, ENV) {
return ENV.dev;
}]); // see beginning and end of square bracket
// also add new injected modules to both the array (surrounded by quotes) and the function

Karma: Angular Module Mocking

I am currently trying to write karma unit tests for a controller in an angular app. For me the desired result is to load ONLY the controller which I am testing (assume for now I only ever want to test this one controller) and have all other dependencies mocked out for me.
To set the stage:
I have a module: 'XYZ' defined in js/xyz_module.js
I have a module: 'ABC' defined in js/abc_module.js
module 'ABC' is dependent on 'XYZ'
module 'ABC' has a controller: 'AbcController' defined in js/abc_controller.js
my testing is to be done ONLY on 'AbcController'
karma.conf.js:
module.exports = function(config) {
config.set({
basePath: '',
frameworks: ['jasmine'],
files: [
'node_modules/angular/angular.js',
'node_modules/angular-mocks/angular-mocks.js',
'js/abc_module.js',
'js/controllers/abc_controller.js',
'spec/karma/abc_controller_spec.js'
],
...
abc_module.js:
angular.module('ABC', ['XYZ']);
abc_controller.js:
angular.module('ABC').controller('AbcController', ['$scope', function($scope) {
$scope.letters = ['a', 'b', 'c']
}]);
abc_controller_spec.js:
describe('AbcController', function() {
beforeEach(function() {
angular.mock.module('XYZ');
module('ABC')
});
var $controller;
beforeEach(inject(function(_$controller_) {
$controller = _$controller_
}));
describe('$scope.letters', function() {
it("is set to three letters", function() {
var $scope = {};
$controller('AbcController', {$scope: $scope});
expect($scope.letters.length).toEqual(3)
})
})
});
My theory was that, since module XYZ is a mocked module that I should NOT have to include js/xyz_module.js in my karam.conf.js file, but that the mocking framework would simply mock it for me. Unfortunately this does not seem to be the case. When I run karma I get an error that it cannot find the module 'XYZ'. Is there any way around this? If I have to include all dependencies even to test a portion of an application, it will make it exponentially more difficult with more complex applications. Obviously this is a contrived example and the project I am actually working with involves many modules with many dependencies, making it exceedingly difficult to test portions of it if I have to effectively include the whole application. I thought that was the whole point of mocking? Am I missing something?
After further research I think I now realise that angular.mock.module does not actually CREATE a module but is used to initialise an existing module. As such, if I create an additional mock file with: angular.module('XYZ',[]);, add it to karma.conf.js and THEN do beforeEach(angular.mock.module('XYZ')); that seems to work. One baby step forward. The next step is mocking any dependencies that 'XYZ' has and that my 'ABC' module uses, but that is probably another question.

Why my AngularJS + RequireJS application is not building via grunt-contrib-requirejs?

I have src of my application. I use AngularJS. I use RequireJS as module loader. I use Grunt as task runner. When I run application using src: everything is good. When I build application with Grunt, application is not working. I got no errors in console.
Main thing I noticed: my code (code of my application: app.js and files under js/) does not appear in output file which is set in grunt task settings. Also, I don't think there is something about AngularJS.
Main config file:
require.config({
paths: {
'angular' : '../components/angular/angular',
/* etc..... */
'jquery': '../components/jquery/dist/jquery',
'application': './app'
},
shim: {
/* etc */
application: {
deps: ['angular']
},
angular: {
exports : 'angular'
}
},
baseUrl: '/js'
});
require(['application', 'angular', 'ngRoute', 'bootstrap' /* ngRoute and bootstrap from etc :) */], function (app) {
app.init();
});
My app in app.js is:
define([
'require', 'angular', 'main/main', 'common/common'
], function (require) {
'use strict';
var angular = require('angular');
var app = angular.module('myApp', ['ngRoute', 'main', 'common']);
app.init = function () {
angular.bootstrap(document, ['myApp']);
};
app.config(['$routeProvider',
function ($routeProvider) {
$routeProvider
./* ... some code */
}
]);
return app;
});
I add main RequireJS config file at the end of body tag:
<script type="text/javascript" src="components/requirejs/require.js" data-main="js/bootstrap.js"></script>
Now I have problem. I have Grunt as build system. I have this task:
grunt.initConfig({
requirejs: {
compile: {
options: {
baseUrl: "public/js",
mainConfigFile: "public/js/bootstrap.js",
name: 'bootstrap',
out: "build/js/bootstrap.js",
optimize: 'none'
}
}
},
// etc
I have no optimisation, so I get ~11k lines of code in output file.
As I said. Main problem is: no AngularJS code and no application code in output file.
Why? I set up mainConfigFile correctly. Problem is in RequireJS config file? But everything is okay, when I am running my app on src.
It would be better if you can provide the exactly error output you get. And where you got it (from browser's console or from terminal during build process)
For now I will suggest some adjustments what could possibly help with your case.
angular: {
exports : 'angular'
}
Here you have already export angular.js into global local variable (inside every require and define block).
And by doing var angular = require('angular'); you are possibly asynchronously override angular variable inside your app.js module.
For 'require' being added into define block, as r.js always reading what module got to be loaded in very first step, and then merged into single file. And this may confuse r.js to merging requireJS into itself.
Suggest this adjustment for your app.js:
define([ // Removed 'require' because no needed , it is already global and usable anywhere
'angular', 'main/main', 'common/common'
], function () {
'use strict';
// var angular = require('angular'); // This is a very common mistake. You are not going to call angular this way, requireJS difference with commonJS.
var app = angular.module('myApp', ['ngRoute', 'main', 'common']);
app.init = function () {
angular.bootstrap(document, ['myApp']);
};
app.config(['$routeProvider',
function ($routeProvider) {
$routeProvider
./* ... some code */
}
]);
return app;
});
And last but not least data-main="js/bootstrap.js" I think it should be js/main.js or a typo.
EDIT added explanations for 'require' in define block, and angular local variable.

AngularJS design guide

Previously when I was writing angular apps, I used to do
angular.module('ngApp', ['all', 'required', 'ng*', 'dependencies'])
in my app.js and then inside services/ and controllers, I could simply do:
angular.module('ngApp')
I have a repo to demonstrate that.
But then I saw the angular-seed/, the way implemented was,
in controllers/
angular.module('appControllers', ['dependencies'])...
in services/
angular.module('appServices', ['dependencies'])..
in app.js
angular.module('ngApp', ['ng*', 'appControllers', 'appSrvices'])..
I had no issue with design, infact I thought it was good, since evrything was dependency injected as well as modular.
I have a situation where I have a services/movie.js that has
angular.module('myAppServices', ['ngResource']).factory(..)
and services/config.js
angular.module('myAppServices').factory(..)
But while writing tests with karma and jasmine. In the karma.conf.js,
I had
files: ['usual', 'bower_components/angular-*.js', 'app/services/**/*.js', '..']
but the problem was config.js got loaded before movie.js and there were errors, myAppServices is not loaded or mis-spelt.
The way I fixed it was I did:
files: ['..', 'app/services/movie.js', 'app/services/config.js']
I have set up a github repo for this too. Here is the controller test file and here is the karma.conf.
I want to know what can be the possible approaches to take such modular approach, without having to specify the order in which the files are to be loaded for my tests.
And this is my first unit test, and its failing:
Error: Unexpected request: GET https://api.themoviedb.org/3/configuration?api_key=2e329c92227ed8be07944ae447c9426f
Expected GET https://api.themoviedb.org/3/movie/top_rated?api_key=2e329c92227ed8be07944ae447c9426f
It would be helpful if I could get some help in fixing that too.
The test
describe('Controllers', function() {
beforeEach(module('myApp'));
beforeEach(module('myAppServices'));
describe("MoviesCtrl", function() {
var scope, ctrl, httpBackend;
beforeEach(inject(function($httpBackend, $rootScope, _$controller_, Movie, Config) {
httpBackend = $httpBackend;
ctrl = _$controller_;
scope = $rootScope.$new();
}));
it("should return a list of movies", function() {
var data = {results: [{name: "Abc"}, {name: "Def"}]};
httpBackend.
expectGET("https://api.themoviedb.org/3/movie/top_rated?api_key=2e329c92227ed8be07944ae447c9426f").
respond(data);
ctrl('MoviesCtrl', { $scope: scope });
httpBackend.flush()
expect(scope.image).toEqual("https://api.themoviedb.org/3/");
});
});
});
conf. file
module.exports = function(config) {
config.set({
basePath: './',
frameworks: ['jasmine'],
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-route/angular-route.js',
'app/services/movie.js',
'app/services/config.js',
'app/controllers/*.js',
'app/app.js',
'unit-tests/**/*.js'
],
exclude: [
'app/**/*.min.js'
],
preprocessors: {
},
reporters: ['progress'],
port: 9876,
colors: true,
logLevel: config.LOG_INFO,
autoWatch: true,
browsers: ['Chrome'],
singleRun: false
});
};
UPDATE
I have figured out the error in test, I had to mock the other http request for the configuration. thanks to #Phil.
This is my test now:
describe('Controllers', function() {
beforeEach(module('myApp'));
beforeEach(module('myAppServices'));
describe("MoviesCtrl", function() {
var scope, httpBackend;
var config_data = { images: { base_url: "http://tmdb.com/t/p", backdrop_sizes: ["w300", "w500"]}},
movie_data = {results: [{name: "Abc"}, {name: "Def"}]};
beforeEach(inject(function($httpBackend, $rootScope, $controller) {
httpBackend = $httpBackend;
scope = $rootScope.$new();
httpBackend.
expectGET("https://api.themoviedb.org/3/configuration?api_key=2e329c92227ed8be07944ae447c9426f").
respond(config_data);
httpBackend.
expectGET("https://api.themoviedb.org/3/movie/top_rated?api_key=2e329c92227ed8be07944ae447c9426f").
respond(movie_data);
$controller('MoviesCtrl', { $scope: scope });
}));
it("should return a list of movies", function() {
expect(scope.image).toEqual({})
httpBackend.flush();
expect(scope.image.backdrop_size).toEqual("w300");
});
});
});
Although I am not sure if this is the right test to do :P . Something like a VCR would be helpful.
Why use two separate files for 10 lines each? The purpose of writing code in separate files is to keep it understandable and maintainable. It would make sense to keep your module 'myAppServices' in one file.
If you really need to break down your code in multiple files, you should make use of dependency injection and make them each a separate module (see my patch against your repo). Then the order of loading stops being an issue.
I still haven't found a angular-ish solution to this problem.
Still there are two ways to deal with it.
Using RequireJS as in #Lukasz 's blog post
and the second one is a dirty one, which I did,
Wrote an _config.js file inside services/, controllers/, directives/, which has, for example angular.module('myAppServices', ['ngResource']) in services/,
And in the karma.config.js I had to do files: ['app/services/_config.js', 'app/controllers/_config.js] . Although the problem still remains because I have to specify the order in which the two _config.js's are to be loaded.
Another way could be to have a single app/config.js file with,
(function() {
angular.module('myAppServices', ['ngResource']);
angular.module('myAppControllers', ['myAppServices']);
}());
and then do files: ['app/config.js', 'app/controllers/**/*.js', 'app/services/**/*.js'] in karma.conf.js

Categories

Resources