Mock KendoUI component using Jasmine - kendo-ui

I have a kendo combobox.
In one of the method I am fetching value from the combobox.
Is it possible to mock the kendoComboBox using jasmine.
var $categoryComboBox = $('#Category').data('kendoComboBox');
var selectedCategory = categoryComboBox.dataItem($categoryComboBox.select());
My jasmine test case is something like
var combo = spyOn($.fn, "data").and.returnValue(dummyElement);
var selectedCat = spyOn($.fn, "select").and.returnValue("1");
var selectedItem = spyOn(combo, "dataItem").and.returnValue({ 'ID': '1', 'ClaimTypeCode': 'WW' });
I am not sure what should I specify as dummyElement for this to work.
Thanks

Sounds like you haven't structured your JavaScript for unit testing. You should separate the DOM from the code, so you can test the code independently of a DOM.
Take a look at KendoUI MVVM library, as this allows you to set up a view model (which knows nothing about the DOM), bind it to the HTML elements and Kendo widgets, leaving you free to test the view model easily.
The way you have your JavaScript set up now is going to make it extremely hard to unit test. The only thing I can think of is to separate the code that gets the value from the Kendo widget from the code that does something with that value, and then test the latter, passing in the value you want to test. Still not really the right way to structure it, but it would work for now.

Related

Kendo UI Scheduler: Hide week-view Time Headers (MVC)

I'm trying to find a way to remove the time headers from the kendo scheduler. I've come across a few ways to do it via css, but they tend to leave the scheduler looking a bit "off".
I found some answers in the kendo docs detailing how to do it out of the box with a javascript implementation, but I'm looking for MVC which I can't seem to find any mention of. I've tried and tried to figure out how to do this, but I can't seem to find the appropriate attributes to set.
The kendo MVC wrappers are effectively ASPX/Razor helper functions that generate a javascript implementation. So assuming the javascript solution in the link you provide contains the solution you need, it should be possible to replicate it using the MVC syntax.
Looking at telerik's solution, they manipulate the DOM with javascript in the dataBinding event for Ungrouped and in dataBound for Grouped. You can specify handlers for these events when declaring the scheduler with MVC syntax:
.Events(e => {
e.DataBound("scheduler_dataBound");
e.DataBinding("scheduler_dataBinding");
})
...and then include the implementation of these functions on the page separately (code lifted from the telerik solution):
<script>
function scheduler_dataBound(e) {
var tables = $(".k-scheduler-times .k-scheduler-table");
//Required: remove only last table in dataBound when grouped
tables = tables.last();
var rows = tables.find("tr");
rows.each(function() {
$(this).children("th:last").hide();
}
function scheduler_dataBinding(e) {
var view = this.view();
view.times.hide();
view.timesHeader.hide();
}
</script>
//for hiding time header
$('#schedulerID').find('.k-scheduler-header-wrap').closest('tr').hide()
//for hiding date header
$(".k-scheduler-layout tr:first .k-scheduler-table").find("tr:eq(0)").hide()

The view area of ckEditor sometimes shows empty at the start

I am using the following directive to create a ckEditor view. There are other lines to the directive to save the data but these are not included as saving always works for me.
app.directive('ckEditor', [function () {
return {
require: '?ngModel',
link: function ($scope, elm, attr, ngModel) {
var ck = ck = CKEDITOR.replace(elm[0]);
ngModel.$render = function (value) {
ck.setData(ngModel.$modelValue);
setTimeout(function () {
ck.setData(ngModel.$modelValue);
}, 1000);
}; }
};
}])
The window appears but almost always the first time around it is empty. Then after clicking the [SOURCE] button to show the source and clicking it again the window is populated with data.
I'm very sure that the ck.setData works as I tried a ck.getData and then logged the output to the console. However it seems like ck.setData does not make the data visible at the start.
Is there some way to force the view window contents to appear?
You can call render on the model at any time and it will simply do whatever you've told it to do. In your case, calling ngModel.$render() will grab the $modelValue and pass it to ck.setData(). Angular will automatically call $render whenever it needs to during its digest cycle (i.e. whenever it notices that the model has been updated). However, I have noticed that there are times when Angular doesn't update properly, especially in instances where the $modelValue is set prior to the directive being compiled.
So, you can simply call ngModel.$render() when your modal object is set. The only problem with that is you have to have access to the ngModel object to do that, which you don't have in your controller. My suggestion would be to do the following:
In your controller:
$scope.editRow = function (row, entityType) {
$scope.modal.data = row;
$scope.modal.visible = true;
...
...
// trigger event after $scope.modal is set
$scope.$emit('modalObjectSet', $scope.modal); //passing $scope.modal is optional
}
In your directive:
ngModel.$render = function (value) {
ck.setData(ngModel.$modelValue);
};
scope.$on('modalObjectSet', function(e, modalData){
// force a call to render
ngModel.$render();
});
Its not a particularly clean solution, but it should allow you to call $render whenever you need to. I hope that helps.
UPDATE: (after your update)
I wasn't aware that your controllers were nested. This can get really icky in Angular, but I'll try to provide a few possible solutions (given that I'm not able to see all your code and project layout). Scope events (as noted here) are specific to the nesting of the scope and only emit events to child scopes. Because of that, I would suggest trying one of the three following solutions (listed in order of my personal preference):
1) Reorganize your code to have a cleaner layout (less nesting of controllers) so that your scopes are direct decendants (rather than sibling controllers).
2) I'm going to assume that 1) wasn't possible. Next I would try to use the $scope.$broadcast() function. The specs for that are listed here as well. The difference between $emit and $broadcast is that $emit only sends event to child $scopes, while $broadcast will send events to both parent and child scopes.
3) Forget using $scope events in angular and just use generic javascript events (using a framework such as jQuery or even just roll your own as in the example here)
There's a fairly simple answer to the question. I checked the DOM and found out the data was getting loaded in fact all of the time. However it was not displaying in the Chrome browser. So the problem is more of a display issue with ckEditor. Strange solution seems to be to do a resize of the ckEditor window which then makes the text visible.
This is a strange issue with ckeditor when your ckeditor is hidden by default. Trying to show the editor has a 30% chance of the editor being uneditable and the editor data is cleared. If you are trying to hide/show your editor, use a css trick like position:absolute;left-9999px; to hide the editor and just return it back by css. This way, the ckeditor is not being removed in the DOM but is just positioned elsewhere.
Use this java script code that is very simple and effective.Note editor1 is my textarea id
<script>
$(function () {
CKEDITOR.timestamp= new Date();
CKEDITOR.replace('editor1');
});
</script>
Second way In controller ,when your query is fetch data from database then use th
is code after .success(function().
$http.get(url).success(function(){
CKEDITOR.replace('editor1');
});
I know, that this thread is dead for a year, but I got the same problem and I found another (still ugly) solution to this problem:
instance.setData(html, function(){
instance.setData(html);
});

Knockout: Scrolling to one of newly inserted elements

So I have an asynchronous call to a controller in c# to obtain say 100 objects.
After this is done I load it into my html page and through a parameter in the link I intend to scroll to it.
So for instance: localhost/page.aspx?scrollToId=85
I do this as follows:
var selectedItem = $("tr[data-scrollToId='" + selectedItemId() + "']");
$('body').scrollTo(selectedItem );
selectedItem.fadeOut().fadeIn();
This works when I have static elements but because of the asynchronous call the dom is loaded way later and it doesnt scroll to it. (It does fade out and in again).
I have been looking at the afterRender method of knockout itself but this too didn't give any change.
Is there a way to wait for the actual dom to render all elements and only after this is done, scroll to it?
Many thanks in advance.
The issue was actually that afterRender only works on templates. I moved my view to a template then afterRender works.

How do I add DOM elements in jasmine tests without using external html files?

I'm writing some simple jasmine tests and I'm getting an exception since the code I'm testing is looking for a form that doesn't exist because there's no DOM when testing a js file only: $("form")[0] in the tested js file leads to:
TypeError: $(...)[0] is undefined
I read a bit about jasmine-jquery and realized I can use some html fixture with an external html file. That flow seems quite messy, since all I need to do is only to add an empty valid form so that the test (which focusing on something else) will run, something like <form></form> appending would be enough I think.
At first I thought that sandbox() function will be the solution, but it seems that it creates only divs, and I need a form.
Any simple way to add some elements by using only code in jasmine spec file?
The simplest solution is to add the form to the DOM by yourself in the before block and then delete it in the after block:
describe(function(){
var form;
beforeEach(function(){
form = $('<form>');
$(document.body).append(form);
});
it('your test', function(){
})
afterEach(function(){
form.remove();
form = null;
});
});
Also writing your sandbox helper isn't that hard:
function sandbox(html){
var el;
beforeEach(function(){
el = $(html);
$(document.body).append(el);
});
afterEach(function(){
el.remove();
el = null;
});
Another approach is to use jasmine fixture
The concept
Here's one way to think about it:
In jQuery, you give $() a CSS selector and it finds elements on the
DOM.
In jasmine-fixture, you give affix() a CSS selector and it adds those
elements to the DOM.
This is very useful for tests, because it means that after setting up
the state of the DOM with affix, your subject code under test will
have the elements it needs to do its work.
Finally, jasmine-fixture will help you avoid test pollution by tidying
up and remove everything you affix to the DOM after each spec runs.
See also: SO: dom manipulation in Jasmine test
You should use sandbox() to create a div and create a form element and append to sandbox, this is the safer way to jasmine take control to this fixtures in the DOM.

Dynamic Elements are not appearing in IE8 until there is a mouse click

I have an Ajax request that returns search results, and I am dynamically creating DOM elements to display those results. This is working as expected in all the browsers I've tested except for IE8.
The request is returning fine, the JavaScript is running successfully, and the elements are being created, but the elements are not being displayed in the page. They only appear after a mouse-click somewhere on the page.
I ran a quick test that ran the callback code without the Ajax request, and it behaved as expected there. So I'm wondering if this has something to do with the way IE8 is managing the callback thread. Has anyone else seen behavior like this, or have insight on it?
The callback is fundamentally very simple. I have reproduced with this:
function catchResults(response) {
var contentBlock = document.getElementById('divResults');
var divResults = document.createElement('div');
var txt = document.createTextNode("Results");
divResults.appendChild(txt);
contentBlock.appendChild(divResults);
}
I am using JQuery.ajax to make the call.
I have seen the proper behavior in FireFox and Chrome.
Thanks for the help!
I ran into this problem not so long ago on IE8.
I think this might be a problem with IE8 not re-rendering the elements in question.
An easy way to confirm this is to add a class to the parent element and then remove it. This should trigger IE8 to re-render the element.
If contentBlock is the parent element then you could test with the following:
Javascript version:
// Variable storing the test class name
var testClass = "testClass";
// Add test class to element
contentBlock.className += " "+testClass;
// Remove test class from element
var reg = new RegExp('(\\s|^)'+testClass+'(\\s|$)');
contentBlock.className=contentBlock.className.replace(reg,' ');
jQuery version:
// Variable storing the test class name
var testClass = "testClass";
// Add test class to element and then remove it
$('#divResults').addClass(testClass).removeClass(testClass);
Just put it at end of the function after you appendChild. Hopefully this should fix your issue.
Reference: http://www.openjs.com/scripts/dom/class_manipulation.php

Resources