I have a javascript function I want to test, containing .load. The function looks like this :
function getPane(divId) {
$("#" + divId).load(
"Pane.html",
function () {
//do some work here
});
});
}
I want to test this using Qunit but I'm not sure how to mock this behaviour.
I also do not know how to mock a function that has both .load and .get -
function getPane(divId) {
$("#" + divId).load("Pane.html", function () {
$.get("/Config/Pane", function (data) {
//do work here
}
});
});
}
I'm using only QUnit, no Mockjax or Sinon.js or anything ( I know,I know I should).
Any help would be appreciated. Thanks.
Since the OP suggested they might go with Mockjax, I figured I'd add that solution. Note that I'm adding the mocks in a setup method and tearing them down after. This allows for each test to be idempotent. Additionally, your getPane() function needs a callback so you can add assertions in your tests.
function getPane(divId, cb) {
$("#" + divId).load("Pane.html", function () {
$.get("/Config/Pane", function (data) {
// do work here
cb(); // callback executed for any additional actions (like tests)
// you may want to add some error handling with callback as well
});
});
}
Then in your #qunit-fixture of the qunit test file add the div to put stuff into:
<html>
...
<body>
<div id="qunit"></div>
<div id="qunit-fixture">
<div id="foobar"></div> <!-- our test element -->
</div>
...
</body>
</html>
Now write your mocks and tests:
QUnit.module("some tests", {
setup: function() {
$.mockjax({
url: "Pane.html",
responseText: "<div>Some HTML content</div>"
});
},
teardown: function() {
$.mockjax.clear(); // new in 1.6
}
});
QUnit.asyncTest("test it out", function(assert) {
getPane("foobar", function() {
assert.equal($("#foobar div").length, 0, "A new div was added to the page!");
QUnit.start();
});
});
Related
I got a problem with mocking the location reload functionality within Jasmine. I tried several methods (method 1 , method 2) to mock any location reload events but with no luck.
My situation is the following thing. I have a rather simple function:
function TestCall(xhr) {
if (xhr === 401) {
location.reload();
}
}
I tried creating the following Jasmine tests:
it("FakeCall", function (){
spyOn(TestCall, 'reload').and.callFake(function(){});
TestCall(401);
expect(TestCall).toHaveBeenCalled(); // this should check if reload functionality have been called
});
I want to mock the location reload function but I have no clue why this does not work. Can anyone guide/tell me what I do wrong?
Total code:
describe("multiple scripts", function () {
describe("2# FakeCall", function() {
function TestCall(xhr) {
if (xhr === 401) {
location.reload();
}
}
it("2.1 # Reload", function (){
spyOn(location, 'reload');
TestCall(401);
expect(location.reload).toHaveBeenCalled();
});
});
});
When you use spyOn, you give object as the first argument and a name of its method (it is attribute of that object) as the second.
So instead of spyOn(TestCall, 'reload') use this spyOn(location, 'reload'). Now it should work.
In your case it could look like this
it("FakeCall", function (){
spyOn(location, 'reload');
TestCall(401);
expect(location.reload).toHaveBeenCalled();
});
Good Day, this maybe a silly question :) how can I pass a parameter to an external javascript function using .on ?
view:
<script>
var attachedPo = 0;
$this.ready(function(){
$('.chckboxPo').on('ifChecked', addPoToBill(attachedPo));
$('.chckboxPo').on('ifUnchecked', removePoToBill(attachedPo ));
});
</script>
external script:
function addPoToBill(attachedPo){
attachedPo++;
}
function removePoToBill(attachedPo){
attachedPo--;
}
but Im getting an error! thanks for guiding :)
You need to wrap your handlers in anonymous functions:
$('.chckboxPo')
.on('ifChecked', function() {
addPoToBill(attachedPo);
})
.on('ifUnchecked', function() {
removePoToBill(attachedPo);
});
You can also chain the calls to on as they are being attached to the same element.
If your intention is to count how many boxes are checked, via passing variable indirectly to functions try using an object instead like this:
JSFiddle: http://jsfiddle.net/TrueBlueAussie/pBkhX/
var attachedPo = {
count: 0
};
$(function () {
$('.chckboxPo')
.on('change', function () {
if ($(this).is(':checked')) {
addPoToBill(attachedPo);
} else {
removePoToBill(attachedPo);
}
$("#output").prepend("" + attachedPo.count + "<br/>");
});
});
function addPoToBill(attachedPo) {
attachedPo.count++;
}
function removePoToBill(attachedPo) {
attachedPo.count--;
}
If it is not doing anything else you can simplify the whole thing to count checked checkboxes:
$(function () {
var attachedPo = 0;
$('.chckboxPo')
.on('change', function () {
attachedPo = $(".chckboxPo:checked").length;
});
});
"DOM Ready" events:
you also needed to wrap it in a ready handler like this instead of what you have now:
$(function(){
...
});
*Note: $(function(){YOUR CODE HERE}); is just a shortcut for $(document).ready(function(){YOUR CODE HERE});
You can also do the "safer version" (that ensures a locally scoped $) like this:
jQuery(function($){
...
});
This works because jQuery passes a reference to itself through as the first parameter when your "on load" anonymous function is called.
There are other variations to avoid conflicts with other libraries (not very common as most modern libs know to leave $ to jQuery nowadays). Just look up jQuery.noConflict to find out more.
I am new to Jasmine and seem to be struggling to get what I think is a fairy standard kind of thing running.
I am loading an HTML file via a fixture and trying to call a click on an element on the dom. This I would expect result in the call to the method of the JS file I am trying to test. When I try and debug this in developer tools the method that should be called in my js file never hits a breakpoint. As such I assume that code is not being called and therfore does not toggle the expand/collapse class.
My test:
describe("userExpand", function () {
beforeEach(function () {
loadFixtures('user-expand.html');
//userControl();
//this.addMatchers({
// toHaveClass: function (className) {
// return this.actual.hasClass(className);
// }
//});
});
//this test works ok
it("checks the click is firing", function () {
spyOnEvent($('.expanded'), 'click');
$('.expanded').trigger('click');
expect("click").toHaveBeenTriggeredOn($('.expanded'));
});
//this doesn't
it("checks the click is changing the class", function () {
//spyOnEvent($('.collapsed'), 'click');
var myElement = $('.collapsed');
myElement.click();
expect(myElement).toHaveClass('.expanded');
});
Part of the fixture:
<div class="wrapper">
<div class="row group">
<div class="col-md-1" data-bordercolour=""> </div>
<div class="collapsed col-md-1"> </div>
<div class="col-md-9">None (1)</div>
The JS I am trying to test:
var userControl = function () {
"use strict";
var collapse = '.collapsed';
var expand = '.expanded';
var userList = $(".userList");
function toggleState() {
var currentControl = $(this);
if (currentControl.hasClass('all')) {
if (currentControl.hasClass('expanded')) {
toggleIcon(currentControl, collapse);
userList.find(".user-group-summary").hide()
.end()
.find(".user-group-info").show();
} else {
toggleIcon(currentControl, expand);
userList.find(".user-group-summary").show()
.end()
.find(".user-group-info").hide();
}
} else {
currentControl.parent().nextUntil('.group').toggle();
currentControl.toggleClass("expanded collapsed");
currentControl.parent().find(".user-group-summary").toggle()
.end()
.find(".user-group-info").toggle();
}
};
function toggleIcon(ctrl, currentState) {
var details = ctrl.closest('div.row').siblings('.wrapper');
details.find(currentState).toggleClass('expanded collapsed');
if (currentState === expand) {
details.find('.detail').hide();
} else {
details.find('.detail').show();
}
}
userList.on('click', '.expanded, .collapsed', toggleState);
$('[data-bordercolour]').each(function () {
$(this).css("background-color", $(this).data('bordercolour'))
.parent().nextUntil('.group')
.find('>:first-child').css("background-color", $(this).data('bordercolour'));
});
return {
toggleState: toggleState
};
}();
The code works fine in normal use so I am sure I am missing something obvious with the way Jasmine should be used. Any help would be appreciated.
Update:
I can make the togglestate method fire by using call in the test rather than triggering a click event:
it('checks on click of icon toggles that icon', function () {
var myElement = $('.collapsed');
userControl.toggleState.call(myElement);
expect(myElement).toHaveClass('expanded');
});
This seems a little strange as all the examples I can find are quite happy with click. Gets me off the hook but I would still like to know what I am missing.
It's hard to give a precise hint without the source code. Does click on .collapsed involve asynchronous action(s)? If so, wrapping the test in runs(...); waitsFor(...); runs(...); may solve the problem. Check the Jasmine introduction for how to do this.
I am trying to pass the elements "event" into a JavaScript function click using jQuery.
INLINE:
This is easy when doing it directly (inline).
Click Me
USING JQUERY:
But how do you do it using jQuery?
<script type="text/javascript">
<!--
function ToggleMinimize(title, e)
{
// ...various execution code would be here ... //
}
jQuery(window).load(function() {
// How do I "get at" the "event" object for the element & pass it into the function definition?
jQuery(".chatDialog-toggle-button").bind('click', ToggleMinimize);
})
-->
</script>
Thanking you ahead of time!
Following on from what a lot of the other guys have shown regarding passing data to an event function, there is a specific method for this, you can pass a full object map this way per bind.
Also bare in mind that jQuery normalises the event object before you receive it, so it is a little different, see: http://api.jquery.com/category/events/event-object/
function ToggleMinimize(e) {
// ...various execution code would be here ... //
// e.data contains the data object map
// e.data.msg == "Hello World"
}
jQuery(window).load(function() {
jQuery(".chatDialog-toggle-button").bind('click', {'msg': "Hello World"}, ToggleMinimize);
});
You don't have to, it's already the first agument, like this:
function ToggleMinimize(e) {
// ...various execution code would be here ... //
}
jQuery(window).load(function() {
jQuery(".chatDialog-toggle-button").bind('click', ToggleMinimize);
});
If you want to also pass a title, then use an anonymous function, like this:
function ToggleMinimize(title, e) {
// ...various execution code would be here ... //
}
jQuery(window).load(function() {
jQuery(".chatDialog-toggle-button").click(function(e) {
ToggleMinimize("some title", e);
});
});
Personally I'd use a data attribute, like this:
Click Me
Then access it like this:
function ToggleMinimize(e) {
var title = jQuery(this).data("title");
}
jQuery(window).load(function() {
jQuery(".chatDialog-toggle-button").click(ToggleMinimize);
});
You can send an anonymous function which executs ToggleMinimize():
jQuery(window).load(function() {
jQuery(".chatDialog-toggle-button").bind('click', function(e) {
ToggleMinimize( "Hello World", e);
});
})
or you can have ToggleMinimize return a function, so you can pass Hello World into it.
function ToggleMinimize(title) {
return function(e){ alert(title); alert(e.target); }
}
jQuery(window).load(function() {
jQuery(".chatDialog-toggle-button").bind('click', ToggleMinimize( "Hello World") );
})
You can do it this way:
$(document).ready(function() {
$('.chatDialog-toggle-button').bind('click', function(event) {
ToggleMinimize(event);
});
});
I've got following JavaScript functions but want to refactor the $(document).ready() as I've got 2 instance of it. How can I achieve this?
FlashMessenger = {
init: function() {
setTimeout(function() {
$(".flash").fadeOut("slow", function () {
$(".flash").remove();
});
}, 5000);
}
}
SelectLanguage = {
init: function() {
$('#selectLanguageId').change(function() {
$('#frmSelectLanguage').submit();
});
}
}
$(document).ready(FlashMessenger.init);
$(document).ready(SelectLanguage.init);
It’s perfectly acceptable to set multiple handlers for $(document).ready, although you may have a good reason to do otherwise that I’m not aware of. You might be interested in knowing that $(handler) can be used as shorthand for $(document).ready(handler):
$(FlashMessenger.init);
$(SelectLanguage.init);
If you really want them in one call though, try this:
$(function() {
FlashMessenger.init();
SelectLanguage.init();
});
First off, there's no reason you have to combine them.
But if you want to:
$(document).ready(function(jq){
FlashMessenger.init(jq);
SelectLanguage.init(jq);
});
Breaking it down:
Create a function to do all your init (it can be named or anonymous; the one above is anonymous).
Have it call the other init functions, passing in the jQuery instance that jQuery passes you just in case they use it.
You might choose to wrap each init call in a try/catch block as well, so that errors in one init don't prevent the next init from occuring, but that depends on your needs.
Just combine them into one call with an anonymous function:
$(document).ready(function()
{
FlashMessenger.init();
SelectLanguage.init();
});
$(document).ready(function() {
FlashMessenger.init();
SelectLanguage.init();
});
Option 1
FlashMessenger = {
init: function() {
setTimeout(function() {
$(".flash").fadeOut("slow", function () {
$(".flash").remove();
});
}, 5000);
}
}
SelectLanguage = {
init: function() {
$('#selectLanguageId').change(function() {
$('#frmSelectLanguage').submit();
});
}
}
$(function(){
FlashMessenger.init();
SelectLanguage.init();
});
Option 2
FlashMessenger = {
init: function() {
setTimeout(function() {
$(".flash").fadeOut("slow", function () {
$(".flash").remove();
});
}, 5000);
}
}
SelectLanguage = {
init: function() {
$('#selectLanguageId').change(function() {
$('#frmSelectLanguage').submit();
});
}
}
$(document).ready(function(){
FlashMessenger.init();
SelectLanguage.init();
});
Option 3
You actually don't need those 2 objects since the only hold the init methods, so here's the ultimate solution, in my opinion, unless you use those objects elsewhere.
$(function(){
$('#selectLanguageId').change(function() {
$('#frmSelectLanguage').submit();
});
setTimeout(function() {
$(".flash").fadeOut("slow", function () {
$(".flash").remove();
});
}, 5000);
})
I prefer 2 and 3 for this reason.
I think what the op is saying is, "If in the future I have a third function to be invoked at document.ready, then how do I do it without touching that piece of code?"
If you do not want multiple $(document).ready() calls, you could just create an array called startupHooks and add each method to it:
startupHooks[ startupHooks.length ] = myNewStartupHook;
and your startup script could look like
$(document).ready(function() {
for( var i=0; i<startupHooks.length; i++ ) {
startupHooks[i]();
}
}
I know that is not mighty useful, but if that appeals to you, you can do it this way.
Personally, I'd go with multiple $(document).ready() calls.
Personally I'd go for not using document.ready at all.
If you place the scripts at the end of your html-page(just before the tag) you can just write in any way you like.
Maybe this doesn't work for 0.01% of the scripts but it never failed to work for me.
Positive effect of this is that the initial HTML+CSS rendering goes faster.
You can also read about it on yahoo. http://developer.yahoo.com/performance/rules.html#js_bottom