Karma tests fail but debug shows pass - angularjs

I'm having an issue where I run my jasmine unit tests in Karma, and one of my unit tests that depends on an external template (AngularJS directive) doesn't pass. When I go into DEBUG mode in the browser and I view the console however, it shows that this unit test is passing and I can confirm this when I step through the unit test in the browser debugger.
I have other unit tests that also depend on external templates that are passing in Karma as expected, but just this particular test is failing in Karma, but passing the in browser console.
Does anyone have any ideas what might be causing this behavior?
EDIT:
Here's what Karma shows:
TypeError: Unable to get value of the property 'then': object is null or undefined
My project handles templates a little differently than AngularJS typically handles them. We have a templateService we created where it retrieves the correct template based on the templateUrl of a directive which looks like this
templateUrl: "navigation-template"
So I've set up Karma to pull the templates and store them in the templateCache. Then I extract the templates with the key Karma used (the file path) and re-insert them into the templateCache with the templateUrl the directive is expecting. Here's how I do it
var template = '<div>' + templateCache.get(templateFilePath) + '</div>'; }
// template files contain multiple templates so this filters out the one I want
template = $("[TemplateId='" + directiveTemplateUrl + "']", template).html();
templateCache.put(directiveTemplateUrl, template);
For all of the other tests, this works fine, but one in particular is producing the error above. What's more interesting is that if I add console logs like this
var template = '<div>' + templateCache.get(templateFilePath) + '</div>';
if (template.indexOf("undefined") > -1) { throw new Error("Template not in cache."); }
template = $("[TemplateId='" + directiveTemplateUrl + "']", template).html();
if (!template) { throw new Error("Template not found."); }
templateCache.put(directiveTemplateUrl, template);
It logs out that the template wasn't found.
Error: Template not in cache.,Error: Unexpected request: GET navigation-template No more request expected
If I DEBUG in the browser (the DEBUG button when Karma loads the browser), I can confirm the template is there and in the browser console (not the command line where Karma is running, but the F12 developer tools in the browser) it logs out that the test was successful like this
LOG: SUCCESS ButtonsDirectives NavigationDirective Should render a loading message
Here's what I know:
There are no significant differences between the failing unit test and the passing ones. It is unrelated to the order in which the unit tests run (this one just happens to run first).

I've figured it out. It turns out the template of the failing unit test contained the following:
<a href="javascript:void(0)" ... > ... </a>
While the unit tests themselves were similar, the failing test is the only template with this particular tag in it.
I was running my unit tests through Karma using IE 9 and javascript:void(0) turns out to have problems in IE 9 (I think it's the browser, not sure if this would cause an issue in other browsers. I cannot confirm with my current setup). Removing javascript:void(0) from the anchor tag resolves my issue.
EDIT:
Confirmed, it is a bug with IE 9. It works fine in Chrome.

Related

Unit tests failing "TypeError: Cannot read property 'element' of undefined" when using md-icon

I have an angular app (using AngularAMD/RequireJS) and I'm converting the design to use angular-material. I have a toolbar directive that has md-icon using md-svg-src.
Trouble running the toolbar-directive_test.js file.
At first it kept complaining "Unresolve GET request images/icon.svg". I resolved in the test setup using:
$httpBackend.whenGET(/\.svg$/).respond('');
Now if I run that test file using fdescribe(...) all the tests pass.
But if I run all my app tests (without fdescribe on that toolbar test file), they are somehow failing because of the md-icons. The error I get, which appears on another test file:
TypeError: Cannot read property 'element' of undefined
The stack-trace does not give any guidance on what is causing it. Only mentions angular-mocks.js, and then exits around the any random assertion on this test file where the error is showing. And if I xdescribe this file which is showing the element of undefined error, the next test file is failing with this error instead.
If I use xdescribe on toolbar-directive_test.js file (the one which has the md-icon), all the tests pass.
And if I run all tests including the toolbar-directive_test.js file, but I remove all the md-icon instances from its template, again all the tests are passing.
I have tried including ngMaterial-mocks package in my tests, and that doesn't do anything. But in general, I'm not using that package (but I am using ngMocks).
Any suggestions on how to debug this?
As #Widget suggested, this works:
$httpBackend.whenGET(/\.svg$/).respond('<svg></svg>');
Where this didn't work:
$httpBackend.whenGET(/\.svg$/).respond('');

Some of your tests did a full page reload - error when running Jasmine tests

I'm running into an issue where when I run my tests on Jasmine, I get this error below. The problem is, it seems to happen when I try to execute a certain amount of tests. It doesn't seem to be tied to a particular test, as if I comment out some, the tests pass. If I uncomment some tests, the error appears. If I comment out ones that were uncommented before, they all pass again. (ie if I have red, green, blue and orange test and it fails, I comment out orange and blue it passes, then I uncomment blue and orange it fails again, but if I comment out red and green it passes again).
Chrome 41.0.2272 (Mac OS X 10.10.1) ERROR Some of your tests did a
full page reload! Chrome 41.0.2272 (Mac OS X 10.10.1): Executed 16 of
29 (1 FAILED) ERROR (0.108 secs / 0.092 secs)
I'm stumped as to what is going on. The more tests I add, that's when this becomes an issue. Has anyone encountered this before? I have no idea what could be causing it, as nothing in any of my tests do any kind of redirection, and they all pass universally on another persons machine.
In my case the problem was that in my source code I had code directly setting the href on the location object, like window.location.href = 'somewhere';
In my specs I set up a onbeforeunload listener that just returns a string instead of allowing the redirect to take place:
beforeAll(() => {
window.onbeforeunload = () => 'Oh no!';
});
I suppose you are using window.location somewhere in your targeted code. In order to pass it just create a spy for the window.onbeforeunload
Example:
window.onbeforeunload = jasmine.createSpy();
Or even better use $window instead, and this will not happen.
Make sure that your tests are properly isolating all modules under test with mocks/spies. The behavior you are seeing says to me that your tests are not truly running in isolation - they are changing some state somewhere that will trigger a reload.
I recently encountered this error with Karma 0.13.12. I upgraded to Karma 0.13.14 and my tests work again. The problem for me (and probably also for #mqklin) was related to https://github.com/karma-runner/karma/issues/1656 and https://github.com/jasmine/jasmine/issues/945.
There are many ways this error can happen.
If your component has a form element, this might be the cause.
Whenever a button on the form is clicked, this error can happen, even tho your component contains no navigation logic.
What worked for me was upgrading Karma from 1.4.0 to 1.4.1 and changing the maximumSpecCallbackDepth in my jasmine.js file from 20 to 100.
creating a spy on the function which has the window.location / reload fixed the issue for me
Hope you have used window.location = "some url" in your code;
Faced similar problem, and solved by using the below changes.
Replaced window.location in the code with,
window.location.assign("some url");
Do the below in unit test:
spyOn(window.location, "assign").and.callFake(() => {
// Dummy assign call - so that your actual call will be faked and the reload will not happen.
});
You also need to make sure that modules are not being loaded twice. In my case, I had an AngularJS module file -e.g., auth.controller.js which contents were already bundled in a core.js file. Once I excluded the bundled files on karma, the error disappeared.
Try to reduce amount of describe sections or completely remove them. I don't know why, but it works for me.
I was using setTimeout(() => window.location.replace('/'), 10);
I used below code in my unit test and it worked for me.
spyOn(global, 'setTimeout');
In case it was ng-submit callback, which doesn't call "event.preventDefault()" and the browser reloads page. Mocking $location doesn't help in that situation.
According to angularjs documentation you should inject the $window module to be able to solve the testability issue you get. If you really want to do a full page refresh while routing, which will reload the whole application. But anyway...
So for example in component
.controller('ExampleController', ['$scope', '$window', function($scope, $window**)
{
$scope.doRerouteWithPageReload = function() {
return this.$window.location.href = "/myUrl";
};
And then in your test-file you import $window to the test-controller your way, then where you assign spies you can do something like this:
$window = { location: {href: jasmine.createSpy() };
And then the actual test is something like this:
expect($window.location.href).toBe("/myUrl");
Angularjs documentation for reading more about $window.
It will solve this Karma redirect error!
var html = '<script type="text/javascript">';
html += 'window.location = "' + urlToRedirect +'"';
html += '</script>';
$( '.wrapper' ).append( html );

view console.log output in angular protractor jasmine test

How can I view console.log output in an angularjs protractor jasmine test? As of now, the browser closes by itself too quickly.
more info - I am working with the angularjs tutorial, step 8. I am trying to change the e2e test to protractor. The protractor config file I'm using is based on %appdata%\npm\node_modules\protractor\referenceConf.js. In spec js files referenced by the config file, I have instances of console.log. However, during execution of the protractor e2e test, the web site opens in chrome, I see things happen in the browser, then the browser closes before I can examine any console.log output. I think I need to keep chrome open somehow. How?
Use browser.manage().logs().get('browser')
browser.manage().logs().get('browser').then(function(browserLogs) {
// browserLogs is an array of objects with level and message fields
browserLogs.forEach(function(log){
if (log.level.value > 900) { // it's an error log
console.log('Browser console error!');
console.log(log.message);
}
});
});
A general misconception is console.log will log the things in your browser. That is incorrect. When you run your tests, along with the results of tests you should see the console.log() values also in the terminal. Browser console is completely different from this.
A general example:
it('get name as John', function(){
element(by.id('name')).getText().then(function(value){
console.log(value);
})
});
Results in Terminal:
John
get name as John - pass
Hope it helps.
This can now be achieved without writing any special code and via plugins:
first party protractor-console-plugin (Chrome only)
third party protractor-console
In order to keep the browser's window open you should try running Protractor in debug mode:
$ <route-to-protractor> debug <route-to-conf-file>
Then in your spec file add this line where you want the execution to stop:
browser.debugger();
In the Protractor debugger console, you can step over the stops by typing c or cont.
More info here: https://github.com/angular/protractor/blob/master/docs/debugging.md
Now, in order to get the console content you can check how to do it in Protractor FAQ:
https://github.com/angular/protractor/blob/master/docs/faq.md#how-can-i-get-hold-of-the-browsers-console
Something like:
browser.manage().logs().get('browser').then(function(browserLog) {
console.log('log: ' + require('util').inspect(browserLog));
})
You may also want to change the logging level to see other types of console outputs.
See my proposed update to the protractor faq to do this here
You could always just override console.log in your test :)
logMessages = [];
console.log = function(message) {
logMessages.push(message);
}
You could also use $log instead of console.log and use a solution like this to put some hooks into the log messages: https://gist.github.com/lrvick/6938531
A simple option is to use:
browser.pause(); protractor/api/browser.pause
for example:
it('should .. test stuff', function() {
browser.pause(); //--->the automation will pause here - follow the instructions in your terminal to continue
//--->your broken testing magic here...
});
Place that method call as the first item in the spec body where you need to view the browsers console.
After the browser pauses you'll have control of the automation. You can then interact with the browser as usual, inspecting elements at different states, checking the browsers console, etc.
Continue the tests by entering c for continue in your terminal - there will be a prompt with instructions waiting for your entry.
If you
install protractor-console
add this to your config
plugins: [
{
package: 'protractor-console',
logLevels: ['severe'],
},
],
this will log all browser error logs to your command line where you run protractor

Testing a AngularJS directive that uses Popcorn.js

I'm working on a new project that uses AngularJS, one of the goals that we have for this project is that it's build using TDD. I'm having some issues testing a directive that uses Popcorn.js to embed video.
Here is a link to some sample code
and here is a sample of the test I'm trying to run,
it('Should load HTML5 video', function() {
var videoLoadedListener = jasmine.createSpy('listener');
rootScope.$on('videoLoaded', videoLoadedListener);
element = angular.element('<player></player>');
element = compile(element)(rootScope);
expect(element.find("video").length).toBe(1);
expect(videoLoadedListener).toHaveBeenCalled();
});
I'm getting an error from the test, 'Specified target player was not found.' which is from Popcorn.js not being able to find the div to insert the video into.
I'm using Karma with both PhantomJS and Chrome. Code seems to work fine, it's just the test that is not working.

Karma (AKA Testacular) rely on ng-app directive?

After spending a day and a half of code battles, i've realized that the e2e tests hanged because i've bootstraped manually and not using ng-app directive
first, FYI.
second, any idea why? How can it be fixed?
thanks
Lior
--
EDIT: here're two plunks that show it, using phone-cat tutorial example:
works in both browser and e2e:
http://plnkr.co/edit/AfLsug1LRi0euKf7TWJa
works interactively in browser, doesnt work in e2e runner:
http://plnkr.co/edit/20OeZ5eV2ZbzgS1qDYfr
Scenario runner appears to need to get a hold of ng-app (for the $injector). I adapted the fix identified here by Vojta Jína and it worked for me.
$(function() {
var myApp = $('#appId'); // Use whatever selector is appropriate here.
angular.bootstrap(myApp, [modules]); // modules is optional - see http://docs.angularjs.org/api/angular.bootstrap
myApp.addClass('ng-app'); // Adds the required ng-app
});
In addition, you may need to put a pause or wait in your beforeAll to allow enough time for manual bootstrapping to complete, e.g.,
beforeEach(function() {
browser().navigateTo('../../index.html');
pause();
});
This fix works but it's not that satisfactory imho. Is there any way to get rid of the pause/wait?

Resources