angularjs triggerHandler is not firing binded event in unit test - unit-testing

with angularJs 1.0.2 I created simple directive that binds to click event on element.
I tried to unittest it with testacular
var linked;
beforeEach(inject(function($rootScope, $compile) {
scope = $rootScope.$new();
var widget_definition = 'click here';
linked = $compile(widget_definition);
}));
it('chceck logic on click', function() {
var button = linked(scope);
// this doesnt work so I give up :/
button.triggerHandler('click');
});
but it tells me that there is no such function defined on button element. but this is already jQ(lite) object and in other tests I can use methods defined for jQlite.
is this a bug in angular??

triggerHandler was added in 1.0.3
Here's a JSFiddle that doesn't throw an exeception http://jsfiddle.net/jaimem/c5Tfw/1/
btw, if you are dealing with UI changes you might want to do e2e tests.

Related

stView.el is undefined while testing Backbone view with jasmine

I'm using Jasmine for unit testing to test an application with Backbone.js (and it's the first time that I'm working with them both so I'm a little bit stuck :/ )
Here is my Backbone view
define(['jquery','backbone','underscore','handelbars','models/story','text!templates/story.html',
'controllers/storyController'],
function($, Backbone, _,handelbars, story,storyTemplate,ctrl){
var View = Backbone.View.extend({
el: '#main',
events:{
'click .close-story' : 'closeStory',
}
// Some functions
});
return View;
});
and the spec of Jasmine
define(['views/storyView'],function (storyView) {
describe("Testing the Story View ",function () {
var stView;
beforeEach(function(){
stView=new storyView({id:1});
stView.render();
})
it("Test if el is defined and trigger the click ",function () {
expect(stView.el).toBeDefined();
})
})
})
Thank you :)
You have hardcode el: '#main' in the view constructor.
This will evaluate when the AMD module is loaded.
And when you run jasmine unit tests, I don't think this element of your application is available in Jasmine test page, unless you have mocked it somehow. You can test this via putting a break point on the constructor and inspecting the DOM.
For the existing code to work, you should attach a dummy element on whatever DOM jasmine is using to run your tests before loading the module containing view definition.
On the other hand, It's better to remove the hardcoded el: '#main' (You can tell the developer that it's a very bad coding practice) and pass the element reference while creating view instance, so you can do
new storyView({id:1, el : $('<div/>'}); // dummy element for test

dangerouslyRenderMarkup(...): Cannot render markup in a worker thread [duplicate]

React Tests Fails after set State causes second render
Up until now testing has been going well with JSDOM and Mocha. So far have not had to test any components that change their state. I found my first issue testing a component that changes it's state.
The Error
1) Reduced Test Case - #current Tests that Fail when Component changes state and renders "before each" hook:
Error: Invariant Violation: dangerouslyRenderMarkup(...): Cannot render markup in a worker thread. Make sure `window` and `document` are available globally before requiring React when unit testing or use React.renderToString for server rendering.
at Context.<anonymous> (test/react-reflux/parts/Reduced-spec.js:47:32)
The Component : Reduced.js
var React = require('react');
var Reduced = React.createClass({
getInitialState() {
console.log("start off with editing as false");
return {editing: false};
},
edit() {
console.log("Setting State to Edit");
this.setState({editing: true});
},
render() {
console.log("Rendering");
return (
<span onClick={this.edit}>
{(this.state.editing) ? "Editing" : "Click To Edit"}
</span>
);
}
});
module.exports = Reduced;
The Tests : 1-pass, 1-fail
var React, TestUtils, jsdom, Reduced, expect;
describe('Reduced Test Case', function () {
before(function () {
jsdom = require('jsdom');
global.document = jsdom.jsdom('<!doctype html><html><body></body></html>');
global.window = global.document.parentWindow;
React = require('react/addons');
TestUtils = React.addons.TestUtils;
Reduced = require('./Reduced');
expect = require('chai').expect;
this.component = TestUtils.renderIntoDocument(
<Reduced />
);
var root = TestUtils.findRenderedDOMComponentWithTag(this.component, 'span');
this.el = root.getDOMNode();
});
describe("Tests Pass without simulate", function () {
it("Root Element Reads 'Click To Edit'", function () {
expect(this.el.innerHTML).to.equal('Click To Edit');
});
});
describe("Tests that Fail when Component changes state and renders", function () {
beforeEach(function () {
//
// Simulate invokes edit, invokes set state, invokes render, then error occures
//
TestUtils.Simulate.click(this.el);
});
it("Root Element Reads 'Editing'", function () {
expect(this.el.innerHTML).to.equal('Editing');
});
});
});
The Results
> mocha --compilers js:babel/register
Reduced Test Case - #current
start off with editing as false
Rendering
Tests Pass without simulate
✓ Root Element Reads 'Click To Edit'
Tests that Fail when Component changes state and renders
Setting State to Edit
Rendering
1) "before each" hook
1 passing (379ms)
1 failing
1) Reduced Test Case Tests that Fail when Component changes state and renders "before each" hook:
Error: Invariant Violation: dangerouslyRenderMarkup(...): Cannot render markup in a worker thread. Make sure `window` and `document` are available globally before requiring React when unit testing or use React.renderToString for server rendering.
at Context.<anonymous> (test/Reduced-spec.js:47:32)
I've been going crazy
Everything is loaded after global.window and global.document
The Simulate Event invokes edit(), then render() before error
All React Mocha JSDOM tests have been working well until this state change issue
Please help ???
The setup JSDOM setup was missing global.navigator.
global.navigator = {
userAgent: 'node.js'
};
Insert your global object modifying(passing window and document objects to global) before React is required.
Because React creates its ExecutionEnvironment object while required and don't modify it while works.

How do I trigger a keyup/keydown event in an angularjs unit test?

I want to unit test a directive that emulates a placeholder, where the input value is cleared only on keyup/down events.
You need to create an event programatically and trigger it. To do so including jQuery for unit tests is quite useful. For example, you could write a simple utility like this:
var triggerKeyDown = function (element, keyCode) {
var e = $.Event("keydown");
e.which = keyCode;
element.trigger(e);
};
and then use it in your unit test like so:
triggerKeyDown(element, 13);
You can see this technique in action in the http://angular-ui.github.io/bootstrap/ project here: https://github.com/angular-ui/bootstrap/blob/master/src/typeahead/test/typeahead.spec.js
Disclaimer: let's be precise here: I'm not advocating using jQuery with AngularJS! I'm just saying that it is a useful DOM manipulation utility for writing tests interacting with the DOM.
To make the above code work without jQuery, change:
$.Event('keydown')
to:
angular.element.Event('keydown')
I had issues with using accepted answer. I found other soultion.
var e = new window.KeyboardEvent('keydown', {
bubbles: true,
cancelable: true,
shiftKey: true
});
delete e.keyCode;
Object.defineProperty(e, 'keyCode', {'value': 27});
$document[0].dispatchEvent(e);
Working example can be found here
I got something like this working.
element.triggerHandler({type:"keydown", which:keyCode});
if you are using angular2, you can trigger any event by calling dispatchEvent(new Event('mousedown')) on HTMLElement instance. for example: Tested with angular 2.rc1
it('should ...', async(inject([TestComponentBuilder], (tcb:TestComponentBuilder) => {
return tcb.createAsync(TestComponent).then((fixture: ComponentFixture<any>) => {
fixture.detectChanges();
let com = fixture.componentInstance;
/* query your component to select element*/
let div:HTMLElement = fixture.nativeElement.querySelector('div');
/* If you want to test #output you can subscribe to its event*/
com.resizeTest.subscribe((x:any)=>{
expect(x).toBe('someValue');
});
/* If you want to test some component internal you can register an event listener*/
div.addEventListener('click',(x)=>{
expect(x).toBe('someOtherValue');
});
/* if you want to trigger an event on selected HTMLElement*/
div.dispatchEvent(new Event('mousedown'));
/* For click events you can use this short form*/
div.click();
fixture.detectChanges();
});
I recently wanted to test this HostListener on a component (Angular 2):
#HostListener('keydown.esc') onEsc() {
this.componentCloseFn();
};
And after searching for some time, this is working:
..
nativeElement.dispatchEvent(new KeyboardEvent('keydown', {'key': 'Escape'}));
...

AngularJS: how to invoke event handlers and detect bindings in tests

I want to write unit and e2e tests for various custom angularjs directives that add javascript event bindings to the elements they are attached to.
In tests, it's easy enough to simulate click and dblclick events using jQuery methods.
element("#id").click();
However, I am also binding mouseover, mouseout and contextmenu events, and haven't found a way to invoke these in e2e tests. The code below shows the approach I am taking.
it('should show a context menu when the user right clicks on a grid row',
function () {
//not currently triggering the context menu
var outerRow = element(".ngRow", "outer row");
var row = element(".ngRow:first > div", "row");
angular.element(row).triggerHandler("contextmenu");
expect(outerRow.attr("class")).toContain("open");
});
How can I get the contextmenu event to fire in tests?
Similarly, in unit tests for the directives, I want to be able to detect if an event binding has been attached to an element.
How can I achieve this?
Got to the bottom of this eventually. To trigger the events on elements selected using jQuery, jQuery obviously needs to be loaded. The problem is that, as explained here, the Angular runner runs the tests in an IFrame which doesn't have jQuery loaded.
However, you can extend the angular scenario dsl to execute code in the context of your e2e test where jQuery is loaded. The function below enables you execute any javascript method, or to fire any event:
//this function extends the Angular Scenario DSL to enable JQuery functions in e2e tests
angular.scenario.dsl('jqFunction', function () {
return function (selector, functionName /*, args */) {
var args = Array.prototype.slice.call(arguments, 2);
return this.addFutureAction(functionName, function ($window, $document, done) {
var $ = $window.$; // jQuery inside the iframe
var elem = $(selector);
if (!elem.length) {
return done('Selector ' + selector + ' did not match any elements.');
}
done(null, elem[functionName].apply(elem, args));
});
};
});
The following code uses the above function to fire the contextmenu event in an e2e test:
it('should show a context menu when the user right clicks on a grid row', function () {
var outerRow = element(".ngRow:first", "outer row");
jqFunction(".ngRow:first > div", "contextmenu");
expect(outerRow.attr("class")).toContain("open");
});

How to unit test Angular controller with $scope.$on?

I have a controller with an event listener in my Angular app, defined as follows.
angular.module('test').controller('TestCtrl', function($rootScope, $scope, testService) {
[...]
$scope.$on("myEvent", function(args, value) {
testService.doStuff(value);
});
});
This works perfectly in the app itself. However, when I try to unit test the functionality of the controller (using Jasmine and Karma), each test method throws the following error:
TypeError: $scope.$on is not a function in [...]/testController.js
I create the controller in my test methods as follows.
it('does stuff', inject(function($rootScope, $controller, testService) {
var scope = $rootScope.$new;
$controller('TestCtrl', {
$scope : scope
});
[...]
}));
I can get around the problem by changing my controller to use rootScope instead:
$rootScope.$on(...)
but then of course the app doesn't work anymore as expected. I can also get rid of the error by introducing the $on method in my test code:
var scope = $rootScope.$new;
scope.$on = function() {};
but mocking the listener like this kind of defeats the purpose of testing my real code.
Why doesn't the test code find the $on method of the scope? (But finds it on rootScope, still...)
There must be something fundamental that I'm missing here but I haven't been able to figure it out even after lots of googling and reading the Angular source code.
$rootScope.$new is a function. You need to invoke it ($rootScope.$new()):
it('does stuff', inject(function($rootScope, $controller, testService) {
var scope = $rootScope.$new();
$controller('TestCtrl', {
$scope : scope
});
[...]
}));
Example plunk: http://plnkr.co/edit/t1x62msmOQDkNItGzcp9?p=preview