testing Angular directive scope with jQuery loaded - angularjs

When I have an Angular directive that uses a controller I've been able to access the directive's scope in my tests by doing this:
element = angular.element('<my-directive></my-directive>');
element = $compile(element)($rootScope);
$rootScope.$digest();
scope = element.scope();
That enables me to set/get scope properties and directly call functions defined by the directive's controller.
However, as soon as I introduced jQuery to the project (required by another dependency I want to use) this stopped working. Specifically in the test element.scope() is returning undefined. Using dev tools I can see that this is not the case when the app is running for real – i.e. I can do $('.blah').scope() and get back a non-undefined value.
Anyone got an idea why this wouldn't work? I've added jQuery to my karma.conf.js (I'm using the Yeoman Angular generator) in the same order as it's included in the HTML file.

I found the problem. I'm running my tests in PhantomJS and was pulling in jQuery 2.0.3. Downgrading to jQuery 1.10.2 made everything work again. I'm kind of surprised that PhantomJS would have a compatibility issue but that seems to have been the problem.

Related

AngularJS -$compileProvider.preAssignBindingsEnabled is not a function

I'm getting the following error message when attempting to do a gulp serve on my AngularJS (ver 1.6.10) app:
Error: [$injector:modulerr] Failed to instantiate module myAppName due to:
$compileProvider.preAssignBindingsEnabled is not a function
#http://localhost:9805/app/scripts/app.js:43:9
invoke#http://localhost:9805/lib/angular/angular.js:5108:16
runInvokeQueue#http://localhost:9805/lib/angular/angular.js:4997:11
loadModules/<#http://localhost:9805/lib/angular/angular.js:5007:11
forEach#http://localhost:9805/lib/angular/angular.js:387:11
loadModules#http://localhost:9805/lib/angular/angular.js:4987:5
createInjector#http://localhost:9805/lib/angular/angular.js:4904:19
doBootstrap#http://localhost:9805/lib/angular/angular.js:1936:20
bootstrap#http://localhost:9805/lib/angular/angular.js:1957:12
angularInit#http://localhost:9805/lib/angular/angular.js:1842:5
#http://localhost:9805/lib/angular/angular.js:35431:5
mightThrow#http://localhost:9805/lib/jquery/dist/jquery.js:3534:21
The calling code looks like this:
/*
* Main module of the application.
*/
angular
.module('dataPipelineApp', [
//various parameters
])
.config(['$compileProvider', '$httpProvider', '$breadcrumbProvider', function ($compileProvider, $httpProvider, $breadcrumbProvider) {
//initialize get if not there
if (!$httpProvider.defaults.headers.get) {
$httpProvider.defaults.headers.get = {};
}
$compileProvider.preAssignBindingsEnabled(true); //err here
$httpProvider.defaults.headers.get['If-Modified-Since'] = 'Mon, 26 Jul 1997 05:00:00 GMT';
// extra
$httpProvider.defaults.headers.get['Cache-Control'] = 'no-cache';
$httpProvider.defaults.headers.get['Pragma'] = 'no-cache';
$compileProvider.debugInfoEnabled(false); // speed up angular performance to not print debug info;
$breadcrumbProvider.setOptions({
templateUrl: 'app/views/headerBar.html'
});
//$httpProvider.defaults.headers.common['Access-Control-Allow-Origin'] = '*';
$httpProvider.interceptors.push("AddToken");
$httpProvider.interceptors.push("UnauthorizeInterceptor");
}])
Similar searches seem to insist that this is a versioning discrepancy. Similar searches for this issue also seem to say there is an issue with the versioning of angular-mocks, however we are not using angular mocks at all. I have tried downgrading my Angular to 1.5.5, which other searches suggest you cannot exceed - despite all my colleagues running this on Angular 1.6.10 or higher. I have also tried using npm to install angular-mocks, despite being unused, and syncing the version to match that of our Angular, but to no avail. I'm really not sure what to do, nor am I sure what's actually happening, why can't it find that function?
EDIT: I also checked in the browser, searching using the console by running a angular.version search - and it turns up 1.7.2 as my Angular version, despite me redoing gulp build and gulp inject serve after npm installing the older versions. It seems it isn't properly choosing the right version - is there something I'm missing to enforce the downgraded installations?
The $compileProvider.preAssignBindingsEnabled flag is deprecated in AngularJS V1.6 and has been removed from AngularJS V1.7.
The AngularJS team strongly recommends migrating your applications to not rely on it as soon as possible. AngularJS V1.6 went end-of-life on 1July2018.
From the Docs:
Due to 38f8c9, directive bindings are no longer available in the constructor.
Previously, the $compileProvider.preAssignBindingsEnabled flag was supported. The flag controlled whether bindings were available inside the controller constructor or only in the $onInit hook. The bindings are now no longer available in the constructor.
To migrate your code:
If you specified $compileProvider.preAssignBindingsEnabled(true) you need to first migrate your code so that the flag can be flipped to false. The instructions on how to do that are available in the "Migrating from 1.5 to 1.6" guide. Afterwards, remove the $compileProvider.preAssignBindingsEnabled(true) statement.
— AngularJS Developer Guide - Migrating to V1.7 - Compile
From the Docs:
Due to bcd0d4, pre-assigning bindings on component/directive controller instances is disabled by default, which means that they will no longer be available inside the constructors. It is still possible to turn it back on, which should help during the migration. Pre-assigning bindings has been deprecated and will be removed in a future version, so we strongly recommend migrating your applications to not rely on it as soon as possible.
Initialization logic that relies on bindings being present should be put in the controller's $onInit() method, which is guaranteed to always be called after the bindings have been assigned.
— AngularJS Developer Guide - Migrating to 1.6 - Compile
Note:
On 1July2018, support for AngularJS 1.6 ended. For more information, see AngularJS MISC - Version Support Status.
It is not supported anymore since AngularJS 1.6. For people who are still migrating their application to a more recent version of AngularJs, If you specified $compileProvider.preAssignBindingsEnabled(true) you need
to first migrate your code so that the flag can be flipped to false. and take a look to this guide https://docs.angularjs.org/guide/migration#migrating-from-1-5-to-1-6
AngularJs commit reference: https://github.com/angular/angular.js/commit/38f8c97af74649ce224b6dd45f433cc665acfbfb

ReferenceError when testing angular service with Karma

I have a simple Angular.js service. It makes use of piece of code called esprima.
Esprima is referenced in index.html like so:
...script src="https://unpkg.com/esprima#~3.1/dist/esprima.js"...
and in my code, it is referenced like so:
var syntax = esprima.parse(jsCode, {tokens: true});
According to Esprima documentation, the CDN reference in html makes it a global variable so esprima usage in my angular.js service should work -- which it does.
However, when trying to exercise my angularjs service code from karma test, I am getting ReferenceError for 'esprima'. I am guessing that Karma has no use for my html file and does not pull in the Esprima package because of that.
I dont know how to "import" Esprima package in such a way that it is seen when used from the browser AND can be seen from Karma. Sorry, I am new to this and drinking from a fire hose here. Thank you
Never mind. Karma.config.js file contains the "files" section where you can list any .js files you want to bring in.

Angular Meteor - Blaze is not defined

I want to use blaze from my angular controller, similar use as
https://github.com/dotansimha/accounts-ui-angular/blob/master/login-buttons-directive.js
Blaze.render(Template.loginButtons, element[0]);
When I try that from my own directive I get - "ReferenceError: Blaze is not defined".
My angular app decleration includes angular-meteor and I also tried to call angular-blaze-template but it's still not being recognized.
I'm not sure why it's working on accounts-ui-angular directive and not on mine

Angularjs + Jquery Draggables testing issue with Karma and Jasmine

I have an angular app that needs a small bit of jquery for complex ui drag and drop operations. To do so I am using jQuery ui draggables.
They work fine, but then I don't know very well how to apply karma/jasmine unit testing to the controllers anymore.
All my other controllers are tested without a problem but this particular controller that has jQuery now inside is the one is giving me problems.
I am receiving this karma error:
INFO [karma]: Karma v0.10.9 server started at http://localhost:9876/
INFO [launcher]: Starting browser PhantomJS
INFO [PhantomJS 1.9.7 (Mac OS X)]: Connected on socket c8iF8rN90zbXSoEYRJzC
PhantomJS 1.9.7 (Mac OS X) ERROR
ReferenceError: Can't find variable: $
I heard that it is better to move the jQuery bits to directives. Could you please guide me in this case?
I am creating tabs in this way inside of my controller. Currently the UI works well, but as I said, it is not testable.
$scope.addSortables = function (){
//ordering locations
$(".dummy-widget .days-container .locations").sortable({
"stop" : function (ui, event){
refreshLocations(ui, event);
PreviewManager.enableTabs();
}
});
}
Thanks a lot
Angular uses jqLite instead of jQuery, but it will happily use jQuery provided that you load it before you load angular. Once you do this you won't need to rely on the global $, angular.element will become your jQuery handle.
So, did you remember to load jQuery in your Karma config? It should look something like the following:
// in karma config
files: [
'vendor/jquery/dist/jquery.js',
'vendor/angular/**/*.js',
'src/app/**/*.js',
'spec/**/*.spec.js'
],
Another thing is that you should try to avoid doing direct DOM manipulation in your controller; use a directive instead. There's a good tutorial on writing angular directives here.
Hope this helps.

Loading mocks into an AngularJS unit test

I'm trying to setup my AngularJS application to test out controllers, routes, templates and so on, but I'm having an issue getting some of the helper methods provided by the angular-mocks.js to work (namely module and inject).
I'm using testacular to load up the test suite with the following files added before the specs:
files = [
MOCHA,
MOCHA_ADAPTER,
'../application/lib/angular.min.js',
'./lib/angular/angular-mocks.js',
'./lib/angular/angular-scenario.js',
'../application/application.js',
'./lib/chai.js',
'./lib/chai-should.js',
'./lib/chai-expect.js',
'./spec/**/*.js'
];
So far so good, but when I run the tests I get this issue:
ReferenceError: Can't find variable: module
Not sure where this is loaded. Am I missing something?
First thing to check is that all those files are getting loaded in the test browser. It's surprisingly easy to get a path wrong in your config and not realize it. If you're running testacular with autowatch, you can navigate to http://localhost:9876/context.html with a browser and use developer tools inspect elements/resources/network and see if anything is missing.
If everything is good there and you're still having problems, post some of your test code and I'll take a look.
UPDATE: It appears (strangely) from the comments in the source for angular-mocks.js (line 1635) that window.module is only available with Jasmine. It looks like you're using Mocha instead of Jasmine. This is very likely the culprit.
ANSWER:
I can't rightly take credit for this Matsko, since you figured it out yourself... but it turns out that the current AngularJS stable download and angular-seed contain an older version of ngMock that doesn't support Mocha. Manually replacing the mock file with the latest from the github repo solves the problem. Glad I could help ;-)
I ran into this issue today and I wanted to provide others with a complete summary of the required steps to get this working. First let's say you have a module named myApp. Inside that that module there is a service called myModel. the myModel service has a method named getItems().
Currently, the angular-mocks.js (I am using AngularJS 1.0.6) does not support Mocha. You will need to visit this link and replace the 1.0.6 version with the one in the master branch from the AngularJS GitHub project. An easy way to do this (if you have wget) is wget https://raw.github.com/angular/angular.js/master/src/ngMock/angular-mocks.js in the correct directory. If you use a properly configured Sublime or vim it can also easily pull it down for you.
Make sure your karma.conf.js file includes the angular-mocks.js file in files array
Somewhere in your tests.js file (maybe at the top level describe) include beforeEach(module('myApp')); or whatever you named your main module.
If you plan to use the service (or whatever you want to include in the test) in more than one place you can call another beforeEach where needed like this:
beforeEach(inject(function(myModel) {
mymodel = myModel;
}));
otherwise you just can inject where it is needed. Now the mymodel variable (notice this is the variable you assigned in the beforeEach above) will be available to you for testing in your next blocks. For example, you can now write:
describe('when fetched', function() {
it('should return 3 items', function() {
// console.log(mymodel.getItems());
expect(mymodel.getItems()).to.have.length(3);
});
});

Resources