Mocking the event object in AngularJS event unit testing - javascript

I have the following test:
it('Should keep location when user rejects confirmation', inject(function ($controller, $rootScope) {
var confirmStub = sinon.stub(),
eventStub = {
preventDefault: sinon.spy()
};
miscServiceStub = function () {
this.confirm = confirmStub;
};
confirmStub.returns(false);
initializeController($controller, 'Builder', $rootScope);
$rs.$broadcast('$locationChangeStart', eventStub);
expect(confirmStub).toHaveBeenCalledOnce();
expect(confirmStub).toHaveBeenCalledWith('Are you sure you want to leave? you will loose any unsaved changes.');
expect(eventStub.stopPropagation).toHaveBeenCalledOnce();
miscServiceStub = function () {};
}));
which tests this code:
$rootScope.$on('$locationChangeStart', function (event) {
dump(arguments);
if (!$miscUtils.confirm('Are you sure you want to leave? you will loose any unsaved changes.')){
event.preventDefault();
}
});
event.$stopPropagation doesn't call the mock event, and dump(arguments) shows that it is being passed into the event right after the real event object:
Chromium 31.0.1650 (Ubuntu) DUMP: Object{
0: Object{name: '$locationChangeStart', targetScope: Scope{$id: ..., $$childTail: ..., $$childHead: ..., $$prevSibling: ..., $$nextSibling: ..., $$watchers: ..., $parent: ..., $$phase: ..., $root: ..., this: ..., $$destroyed: ..., $$asyncQueue: ..., $$postDigestQueue: ..., $$listeners: ..., $$isolateBindings: ..., activeTab: ..., routeParams: ...}, preventDefault: function () { ... }, defaultPrevented: false, currentScope: Scope{$id: ..., $$childTail: ..., $$childHead: ..., $$prevSibling: ..., $$nextSibling: ..., $$watchers: ..., $parent: ..., $$phase: ..., $root: ..., this: ..., $$destroyed: ..., $$asyncQueue: ..., $$postDigestQueue: ..., $$listeners: ..., $$isolateBindings: ..., activeTab: ..., routeParams: ...}},
1: Object{stopPropagation: spy}
}
how can I make it so the event object is the mock event and not the real event object itself? Am I approaching this the right way? I'm quite new to Angular and any comments on the code/test would be greatly appreciated.
If you need any more related code please tell me.

$scope.$broadcast returns the Event object, so you can do this:
var event = $scope.$broadcast("someEvent");
expect(event.defaultPrevented).toBeTruthy();

In this line:
$rs.$broadcast('$locationChangeStart', eventStub);
you provide an argument that will be transmittet alongside with the event, not the event itself. That's why you will get here:
$rootScope.$on('$locationChangeStart', function (event)
2 objects as arguments. The full signature for your event should be:
$rootScope.$on('$locationChangeStart', function (event, eventStub)
So: you can't test the call of stopPropagation in the way you have tried it.
If you have a look at the angular src (line 12185...) you will see, that the event is created without any possibility to mock this object. And the $scope itself is not mocked by angular-mock.
If one want to test that preventDefault is called, i would write a service that has a function that calls preventDefault. This service can easily be spyed on.

Related

why are the views not updated when I console.log(course) the second time

var course = new Object();
var course = {
title: "JavaScript Essential Training",
instructor: "Morten Rand-Hendriksen",
level: 1,
published: true,
views: 0,
updateViews: function() {
return ++course.views;
},
};
console.log(course);
console.log(course);
You are assigning an empty object to the variable course.
var course = new Object();
Now you are assigning an object with properties into it.
var course = {
title: "JavaScript Essential Training",
instructor: "Morten Rand-Hendriksen",
level: 1,
published: true,
views: 0,
updateViews: function() {
return ++course.views;
},
};
Here an object is assigned to variable course and updateViews method will not call as this is an initialization. So if you want to update views you should call updateViews from your variable course. Like this
console.log(course.updateViews());
It would help if you could post what you actually see. However, surely you want to call updateViews() to change the views. Just asking it to write out the current value of the object does not call each method on the object.
Just console.logging the object does not update the views.
Also your first declaration is not useful.
If you want you could do this:
var course = {
title: "JavaScript Essential Training",
instructor: "Morten Rand-Hendriksen",
level: 1,
published: true,
views: 0,
updateViews: function() {
return ++course.views;
},
toString : function() { this.updateViews(); return this }
};
console.log(course.toString());
console.log(course.toString());
I found a more detailed answer
Does console.log invokes toString method of an object?

Unable to write unit test case for button click event

I am working on the React JS application. Whenever the user presses Enter I am firing some event as below.
dispatchSearch() {
if (!this.isSearchButtonDisabled()) {
const searchTerms = { [this.state.searchType]: this.state.searchTerm };
const searchDbTarget = '';
const options = {};
this.props.searchParameters(searchTerms, searchDbTarget, options, this.state.allOpenStoresSearchTerms.selectedOption);
}
}
The above code works as expected. Below is my unit test case for the above code.
it('should dispatch a keycode when keycode value is set', () => {
component.setState({ searchTerm: 'some-value', allOpenStoresSearchTerms: {selectedOption: {value: 'true', label: 'Open Stores'}} });
component.find('SearchInput').props().onKeyPress({ key: 'Enter' });
expect(dispatchEvent).toBeCalled();
expect(dispatchEvent).toBeCalledWith({ simsKeycode: 'some-value', allOpenStoresSearchTerms: {selectedOption: {value: 'true', label: 'Open Stores'}} });
});
This unit test case fails and throws below error
Expected mock function to have been called with:
{"allOpenStoresSearchTerms": {"selectedOption": {"label": "Open Stores", "value": "true"}}, "simsKeycode": "some-value"} as argument
1, but it was called with {"simsKeycode": "some-value"}.
undefined as argument 2, but it was called with "".
undefined as argument 3, but it was called with {}.
undefined as argument 4, but it was called with {"label": "Open Stores", "value": "true"}.
May I know what I am missing in the above code? Any help would be greatly appreciated. Thanks
setState method works only when you're mount rendering your component. it doesn't work on shallow render.

Array attribute of scope in directive is emptied upon reference

My directive looks like this:
angular.module('app')
.directive('chart', function () {
return {
template: '<div class="chart"></div>',
restrict: 'E',
replace: 'true',
scope: {
data: '='
},
link: function (scope, element, attrs) {
console.log(scope);
console.log(scope.data);
}
};});
and gets passed an array from my controller in the view.
<chart data="array"></chart>
The console output looks as follows:
Scope {...}
$id: "006"
$parent: Child
$root: Scope
...
data: Array[1]
0: 10300
length: 1
__proto__: Array[0]
this: Scope
__proto__: Object
and
[]
When the scope object is displayed in the console it has an 'data' attribute with length 1 and the entry '10300', but when scope.data is printed it is just an empty array '[]'.
Why? I am very confused :)
The problem was that the array was initialized in the controller as [] and then filled through a function which was called with $watch. For some reason the two console outputs seem to print different states of the controller. Solution was to call the function once in the controller to initialize the array with values straight away.

EmberJS: Has no method lastObject, nextObject

i was wondering how to call lastObject, nextObject on my Model/Store? I always get a 'Has no method'-Error.
Both methods are defined here:
http://emberjs.com/api/classes/Ember.Array.html
App.Photo = DS.Model.extend
url: DS.attr('string'),
displayName: DS.attr('string')
App.Store = DS.Store.extend
revision: 12,
adapter: 'DS.FixtureAdapter'
App.PhotoController = Ember.ObjectController.extend
init: ->
console.info "last", App.Photo.lastObject
console.info "next", App.Photo.nextObject(#get('content'))
Update/Working Solution
console.info "last", App.Photo.find().get('lastObject')
App.Photo is not an array. It's a class.
App.Photo.find() will return an array-like object, a record array of all photos in the store, which will have a lastObject property, but you won't be able to call it like a function. Furthermore, #nextObject is overridden on arrays to be equivalent to #objectAt, so that won't work either (it doesn't work 'like that' either way, actually).

backbonejs model.toJSON() can't get correct output, with backbone.iobind

backbone Model,board:
define([
'underscore',
'backbone',
'collections/lists',
'iobind',
'iosync'
], function( _, Backbone, Lists,ioBind,ioSync) {
var BoardModel = Backbone.Model.extend({
urlRoot: 'board',
noIoBind: false,
socket: io.connect(''),
idAttribute: '_id',
defaults: {
title: 'One Thousand and One Nights'
},
initialize: function() {
this.id = 1;
this.lists = new Lists;
this.socket.emit('joinBoard',this.id);
_.bindAll(this, 'getBoard');
this.ioBind('initBoard', this.getBoard, this);
},
getBoard: function(data){
this.set(data.data.board[0]);
}
});
return BoardModel;
});
backbone View: boardView:
var IndexView = Backbone.View.extend({
// Instead of generating a new element, bind to the existing elements in the HTML.
el: '#board',
// Board template html
template: Mustache.render(Template.board),
events: {
},
initialize: function() {
//Init Data
this.model = new Board();
// var lists = {
// lists: [
// {name: "To Do",
// cards:[
// {name: "Art work for A."},
// {name: "B Prototype."},
// {name: "C prototype."}
// ]
// },
// {name: "Doing",
// cards: [
// {name: "Art work for A."}
// ]
// },
// {name: "Done"}
// ]
// }
// var partial = {card: Template.card_in_list};
// var listHtml = Mustache.render(Template.list,lists,partial);
// template = $(this.template).find('.list-area').append(listHtml);
},
render: function() {
console.log(this.model);
console.log(this.model.toJSON());
var partial = {card: Template.card_in_list};
var listHtml = Mustache.render(Template.list,this.model,partial);
template = $(this.template).find('.list-area').append(listHtml);
this.$el.html(template);
}
});
in View function: render function, the console.log get different result.
console.log(this.model) can get correct object result:
child
_callbacks: Object
_changing: false
_escapedAttributes: Object
_ioEvents: Object
_pending: Object
_previousAttributes: Object
_silent: Object
attributes: Object
__v: 0
_id: "50b750a7795f285d4e000014"
created: "2012-11-29T12:10:15.269Z"
description: "simple is better, but not simpler"
dueDate: "2012-11-29T12:10:15.269Z"
lists: Array[6]
status: true
title: "test board unique"
__proto__: Object
changed: Object
cid: "c1"
getBoard: function () { [native code] }
id: "50b750a7795f285d4e000014"
lists: child
__proto__: ctor
but this.model.toJSON() only get model default values:
Object
title: "One Thousand and One Nights"
__proto__: Object
it confuse me. anyone know why reason the same model get different result.
In a Backbone Model, your business values (description, title ...) are store in the attributes attribute. When you call toJSON() on your model, what it does is it takes the attributes values, and remove the Backbone.Model object framework's functions and attributes.
When you manually want to set model attributes, you want to use set. I don't know what is in you data.data object, so you should check the doc : http://backbonejs.org/#Model-set
set model.set(attributes, [options])
Set a hash of attributes (one or
many) on the model. If any of the attributes change the models state,
a "change" event will be triggered, unless {silent: true} is passed as
an option. Change events for specific attributes are also triggered,
and you can bind to those as well, for example: change:title, and
change:content. You may also pass individual keys and values.
note.set({title: "March 20", content: "In his eyes she eclipses..."});
book.set("title", "A Scandal in Bohemia"); If the model has a validate
method, it will be validated before the attributes are set, no changes
will occur if the validation fails, and set will return false.
Otherwise, set returns a reference to the model. You may also pass an
error callback in the options, which will be invoked instead of
triggering an "error" event, should validation fail. If {silent: true}
is passed as an option, the validation is deferred until the next
change.
I found i trigger boardView.render twice. when i change code:
a = new boardView;
a.render();
to
a = new boardView;
i got the thing done.
by the way thanks Marcel Falliere's comments.

Categories