I have set up a maven project to use the jasmin-maven-plugin for testing my javascript code. The plugin is set up like this:
<plugin>
<groupId>com.github.searls</groupId>
<artifactId>jasmine-maven-plugin</artifactId>
<version>1.2.0.0</version>
<extensions>true</extensions>
<executions>
<execution>
<goals>
<goal>test</goal>
</goals>
</execution>
</executions>
<configuration>
<jsSrcDir>src/main/webapp/js</jsSrcDir>
<jsTestSrcDir>src/test/javascript</jsTestSrcDir>
<sourceIncludes>
<include>jquery-1.8.2.min.js</include>
<include>jquery-ui-1.8.13.custom.min.js</include>
<include>jquery.json-2.2.js</include>
<include>stylesheet.js</include>
<include>**/*.js</include>
</sourceIncludes>
</configuration>
</plugin>
I have written a javascript method that makes use of jQuery for event handling. The code is like this:
registerDragSurface: function ( element, onDrag ) {
// Wrap every drag surface registration in a closure
// to preserve the given set of arguments as local variables
(function($, element, dragdropsystem, onDrag) {
// Register a mousemove event on the drag surface
$(element).mousemove (function ( event ) {
// If the user is dragging
if (dragdropsystem.isDragging) {
// Call the onDrag method with
// the given element and event
onDrag (element, event);
}
});
// Register a mouseup event on the drag surface
$(element).mouseup (function ( event ) {
// If the user is dragging
if (dragdropsystem.isDragging) {
// Call the onDragEnded function
dragdropsystem.onDragEnded();
}
// We are not dragging anymore, and
// the left mousebutton has been released
dragdropsystem.isDragging = false;
dragdropsystem.isMouseDown = false;
});
})($, element, this, onDrag);
},
I have then written a jasmine spec to test the function. The code is here:
describe("linkedforms.dragdrop", function() {
var dragdrop = linkedforms.dragdrop;
it("test that a drag surface can be registered and onDragCallback is called", function () {
var dragSurface = {};
var onDragCallback = jasmine.createSpy("onDragCallback");
dragdrop.registerDragSurface(dragSurface, onDragCallback);
dragdrop.isDragging = true;
jQuery(dragSurface).trigger("mousemove");
expect(onDragCallback).toHaveBeenCalled();
expect(dragdrop.isDragging).toBe(true);
jQuery(dragSurface).trigger("mouseup");
expect(dragdrop.isDragging).toBe(false);
});
});
I can run mvn jasmine:bdd from the command line, and then visit
http://localhost:8234
to run the tests. This works fine, and all my specs pass. I then try to run the tests from maven using mvn test, but then it breaks. It says :
* Expected spy onDragCallback to have been called.
* Passed.
* Expected true to be false.
This makes me suspect that the jQuery event system does not work properly when run from mvn test and thus in the HtmlUnit browser. Does anybody know how to fix this?
I figured out that it works if I change the jQuery implementation from jquery-1.8.2.min.js [production] to jquery-1.8.2.js [development].
Anyone have any ideas why this is so?
Related
im trying to develop a plugin that render a datagrid to html element. For example in:
<div id="datagrid">
<!-- Renderizado via Backbone.js -->
</div>
I have this plugin definition:
// csDatagrid.js
(function($) {
$.fn.csDatagrid = function(options) {
// Code ...
};
}, (jQuery));
And i call the function in this way:
// csAuditarSesiones.js
$("#datagrid").csDatagrid({
columns: cols,
url: $("#sfAction").val(),
filterBy: 'user_str'
});
Chrome says:
Uncaught TypeError: Object [object Object] has no method 'csDatagrid'
Load library queue (Chrome Developer Tools):
JQuery
csDatagrid (my plugin)
csAuditarSesiones (script with code for the current page, have the plugin call)
Thanks !
EDIT 1
Apparently the plugin not load, follow code always print "Not Loaded !":
if(jQuery().csDatagrid) {
console.log("Loaded !");
}else{
console.log("Not Loaded !");
}
The reason here is you are defining your plugin in the document ready event. Therefore the elements calling this plugin must call the plugin after the plugin has been loaded.
Try defining your new plugin outside of a load\ready event such as.
(function($) {
$.fn.csDatagrid = function(options) {
// Code ...
};
}, (jQuery));
$(document).ready(function(){
$("#datagrid").csDatagrid({
columns: cols,
url: $("#sfAction").val(),
filterBy: 'user_str'
});
});
Just for refrence (surely reading it now). http://learn.jquery.com/plugins/basic-plugin-creation/
EDIT:
Is it possible you have multiple jQuery versions being loaded? This can happen when there are conflicts. Also ensure that your plugin is loaded after the javascript file and before the document ready function.
There is one other (i have no idea why I believe it is when multiple versions are loaded) phenomenom that happens and you have to add the $ call back to your ready function. Such as.
$(document).ready(function($){
//TODO: Code here
});
There is error in your immediate invoked function's last line.
}, (jQuery)); should be :
})(jQuery);
I am trying to test a flow
1.Ajax Request > Loader is visible
2.Response Received > a.Loader is hidden
b.Redirect to another page(where a interstitial is visible)
White testing them with casperJS I use the waitFor method, something like this.
casper.waitFor(function check() {
return this.evaluate(function() {
return $("#loader").is(":hidden");
});
}, function then() {
this.test.pass("Ajax request");
this.waitDone();
}, function timeout() { // step to execute if check has failed
this.echo("Timeout: page did not load in time...").exit();
},4000);
The thing is even if the condition is passed in check, then is not executed until the page is not redirected(read the flow, I am trying to test) and the test suite won't move to the next step.
Is there something I am missing here ?
Make sure your reference 'this' is located within a casper block:
casper.then(function() {
this.waitFor(function check() {
Possible quick fix without knowing more detail. Pass jQuery variable to evaluate.
this.evaluate(function($) {
You could also try:
casper.waitWhileVisible('#loader', function() {
// executes when #loader is hidden
});
docs located at: CasperJS waitWhileVisible
I'm triyng to build a simple animation jQuery-plugin. The main idea is to take an element and manipulate it in some way repeatedly in a fixed intervall which would be the fps of the animation.
I wanted to accomplish this through events. Instead of using loops like for() or while() I want to repeat certain actions through triggering events. The idea behind this: I eventualy want to be able to call multiple actions on certain events, like starting a second animation when the first is done, or even starting it when one animation-sequence is on a certain frame.
Now I tried the following (very simplified version of the plugin):
(function($) {
$.fn.animation = function() {
obj = this;
pause = 1000 / 12; //-> 12fps
function setup(o) {
o.doSomething().trigger('allSetUp');
}
function doStep(o, dt) {
o.doSomething().delay(dt).trigger('stepDone');
}
function sequenceFinished(o) {
o.trigger('startOver');
}
function checkProgress(o) {
o.on({
'allSetup': function(event) {
console.log(event); //check event
doStep(o, pause);
},
'stepDone': function(event) {
console.log(event); //check event
doStep(o, pause);
},
'startOver': function(event) {
console.log(event); //check event
resetAll(o);
}
});
}
function resetAll(o) {
/*<-
reset stuff here
->*/
//then start over again
setup(o);
}
return this.each(function() {
setup(obj);
checkProgress(obj);
});
};
})(jQuery);
Then i call the animation like this:
$(document).ready(function() {
$('#object').animation();
});
And then – nothing happens. No events get fired. My question: why? Is it not possible to use events like this inside of a jQuery plugin? Do I have to trigger them 'manualy' in $(document).ready() (what I would not prefer, because it would be a completely different thing – controling the animation from outside the plugin. Instead I would like to use the events inside the plugin to have a certain level of 'self-control' inside the plugin).
I feel like I'm missing some fundamental thing about custom events (note: I'm still quite new to this) and how to use them...
Thx for any help.
SOLUTION:
The event handling and triggering actually works, I just had to call the checkProgress function first:
Instead of
return this.each(function() {
setup(obj);
checkProgress(obj);
});
I had to do this:
return this.each(function() {
checkProgress(obj);
setup(obj);
});
So the event listening function has to be called before any event gets triggered, what of course makes perfect sense...
You need set event on your DOM model for instance:
$('#foo').bind('custom', function(event, param1, param2) {
alert('My trigger')
});
$('#foo').on('click', function(){ $(this).trigger('custom');});
You DOM element should know when he should fire your trigger.
Please note that in your plugin you don't call any internal function - ONLY DECLARATION
I have buttons that trigger jQuery validation. If the validation fails, the button is faded to help draw attention away from the button to the validation messages.
$('#prev,#next').click(function (e)
{
var qform = $('form');
$.validator.unobtrusive.parse(qform);
if (qform.valid())
{
// Do stuff then submit the form
}
else
{
$('#prev').fadeTo(500, 0.6);
$('#next').fadeTo(500, 0.6);
}
That part works fine.
However, I would like to unfade the buttons once the invalid conditions have been cleared.
Is it possible to hook into jQuery Validation to get an appropriate event (without requiring the user to click a button)? How?
Update
Based on #Darin's answer, I have opened the following ticket with the jquery-validation project
https://github.com/jzaefferer/jquery-validation/issues/459
It might sound you strange but the jQuery.validate plugin doesn't have a global success handler. It does have a success handler but this one is invoked per-field basis. Take a look at the following thread which allows you to modify the plugin and add such handler. So here's how the plugin looks after the modification:
numberOfInvalids: function () {
/*
* Modification starts here...
* Nirmal R Poudyal aka nicholasnet
*/
if (this.objectLength(this.invalid) === 0) {
if (this.validTrack === false) {
if (this.settings.validHandler) {
this.settings.validHandler();
}
this.validTrack = true;
} else {
this.validTrack = false;
}
}
//End of modification
return this.objectLength(this.invalid);
},
and now it's trivial in your code to subscribe to this event:
$(function () {
$('form').data('validator').settings.validHandler = function () {
// the form is valid => do your fade ins here
};
});
By the way I see that you are calling the $.validator.unobtrusive.parse(qform); method which might overwrite the validator data attached to the form and kill the validHandler we have subscribed to. In this case after calling the .parse method you might need to reattach the validHandler as well (I haven't tested it but I feel it might be necessary).
I ran into a similar issue. If you are hesitant to change the source as I am, another option is to hook into the jQuery.fn.addClass method. jQuery Validate uses that method to add the class "valid" to the element whenever it is successfully validated.
(function () {
var originalAddClass = jQuery.fn.addClass;
jQuery.fn.addClass = function () {
var result = originalAddClass.apply(this, arguments);
if (arguments[0] == "valid") {
// Check if form is valid, and if it is fade in buttons.
// this contains the element validated.
}
return result;
};
})();
I found a much better solution, but I am not sure if it will work in your scenario because I do not now if the same options are available with the unobtrusive variant. But this is how i did it in the end with the standard variant.
$("#form").validate({
unhighlight: function (element) {
// Check if form is valid, and if it is fade in buttons.
}
});
In order to increase test coverage of my project, I'm starting to build tests for existing JS code.
One of the existing modules puts the window height in a variable with jQuery:
var window_height = jQuery(window).height();
Internally jQuery uses the clientHeight property.
Now with either mvn clean install or mvn -o test I'm getting the error:
Cannot read property "clientHeight" from null
I assume this is because the "virtual browser" that Jasmine creates doesn't have the window property. Is there any way to make this work?
I can't vouch for how safe it is to override jQuery's methods, but here's how you can use jasmine's spyOn function to override what $(window).height() returns.
it("can override jquerys height function", function() {
var original = $.prototype.height;
spyOn($.prototype, 'height').andCallFake(function() {
if (this[0] === window) {
return 5; // whatever you want the window height to appear to be
} else {
return original.apply(this, arguments);
}
});
// window height gets overridden
expect($(window).height()).toEqual(5);
// everything else uses the actual jQuery call
expect($("body").height()).toBeGreaterThan(5);
});
Alternatively, it would be safer create your own getWindowHeight() function somewhere and just spyOn that with a fake.
Thanks Derek for your answer, the idea of overwriting jQuery.height did the trick, but not in a spy.
(function(){
$.prototype.height = function() {
var original = $.prototype.height;
if (this[0] === window) {
return 800; // Return mock window height
} else {
return original.apply(this, arguments);
}
}
})();
In the pom.xml I include this file directly after jQuery in the .