(function() {
module('call-remote-callbacks', {
setup: function() { $('#qunit-fixture').append($('<form />', { action: '/echo', method: 'get', 'data-remote': 'true' })) }, teardown: function() { $(document).undelegate('form[data-remote]', 'ajax:beforeSend') $(document).undelegate('form[data-remote]', 'ajax:before') $(document).undelegate('form[data-remote]', 'ajax:send') $(document).undelegate('form[data-remote]', 'ajax:complete') $(document).undelegate('form[data-remote]', 'ajax:success') $(document).unbind('ajaxStop') $(document).unbind('iframe:loading') }
})
function start_after_submit(form) {
form.bindNative('ajax:complete', function() { ok(true, 'ajax:complete') start() })
}
function submit(fn) {
var form = $('form') start_after_submit(form) if (fn) fn(form) form.triggerNative('submit')
}
function submit_with_button(submit_button) {
var form = $('form') start_after_submit(form) submit_button.triggerNative('click')
}
asyncTest('modifying form fields with “ajax:before” sends modified data in request', 4, function() {
$('form[data-remote]') .append($('<input type="text" name="user_name" value="john">')) .append($('<input type="text" name="removed_user_name" value="john">')) .bindNative('ajax:before', function() { var form = $(this) form .append($('<input />', {name: 'other_user_name', value: 'jonathan'})) .find('input[name="removed_user_name"]').remove() form .find('input[name="user_name"]').val('steve') }) submit(function(form) { form.bindNative('ajax:success', function(e, data, status, xhr) { equal(data.params.user_name, 'steve', 'modified field value should have been submitted') equal(data.params.other_user_name, 'jonathan', 'added field value should have been submitted') equal(data.params.removed_user_name, undefined, 'removed field value should be undefined') }) })
})
asyncTest('modifying data(“type”) with “ajax:before” requests new dataType in request', 2, function() {
$('form[data-remote]').data('type', 'html') .bindNative('ajax:before', function() { this.setAttribute('data-type', 'xml') }) submit(function(form) { form.bindNative('ajax:beforeSend', function(e, xhr, settings) { equal(settings.dataType, 'xml', 'modified dataType should have been requested') }) })
})
asyncTest('setting data(“with-credentials”,true) with “ajax:before” uses new setting in request', 2, function() {
$('form[data-remote]').data('with-credentials', false) .bindNative('ajax:before', function() { this.setAttribute('data-with-credentials', true) }) submit(function(form) { form.bindNative('ajax:beforeSend', function(e, xhr, settings) { equal(settings.withCredentials, true, 'setting modified in ajax:before should have forced withCredentials request') }) })
})
asyncTest('stopping the “ajax:beforeSend” event aborts the request', 1, function() {
submit(function(form) { form.bindNative('ajax:beforeSend', function() { ok(true, 'aborting request in ajax:beforeSend') return false }) form.unbind('ajax:send').bindNative('ajax:send', function() { ok(false, 'ajax:send should not run') }) form.unbind('ajax:complete').bindNative('ajax:complete', function() { ok(false, 'ajax:complete should not run') }) form.bindNative('ajax:error', function(e, xhr, status, error) { ok(false, 'ajax:error should not run') }) $(document).bindNative('ajaxStop', function() { start() }) })
})
function skipIt() {
// This test cannot work due to the security feature in browsers which makes the value // attribute of file input fields readonly, so it cannot be set with default value. // This is what the test would look like though if browsers let us automate this test. asyncTest('non-blank file form input field should abort remote request, but submit normally', 5, function() { var form = $('form[data-remote]') .append($('<input type="file" name="attachment" value="default.png">')) .bindNative('ajax:beforeSend', function() { ok(false, 'ajax:beforeSend should not run') }) .bind('iframe:loading', function() { ok(true, 'form should get submitted') }) .bindNative('ajax:aborted:file', function(e, data) { ok(data.length == 1, 'ajax:aborted:file event is passed all non-blank file inputs (jQuery objects)') ok(data.first().is('input[name="attachment"]'), 'ajax:aborted:file adds non-blank file input to data') ok(true, 'ajax:aborted:file event should run') }) .triggerNative('submit') setTimeout(function() { form.find('input[type="file"]').val('') form.unbind('ajax:beforeSend') submit() }, 13) }) asyncTest('file form input field should not abort remote request if file form input does not have a name attribute', 5, function() { var form = $('form[data-remote]') .append($('<input type="file" value="default.png">')) .bindNative('ajax:beforeSend', function() { ok(true, 'ajax:beforeSend should run') }) .bind('iframe:loading', function() { ok(true, 'form should get submitted') }) .bindNative('ajax:aborted:file', function(e, data) { ok(false, 'ajax:aborted:file should not run') }) .triggerNative('submit') setTimeout(function() { form.find('input[type="file"]').val('') form.unbind('ajax:beforeSend') submit() }, 13) }) asyncTest('blank file input field should abort request entirely if handler bound to "ajax:aborted:file" event that returns false', 1, function() { var form = $('form[data-remote]') .append($('<input type="file" name="attachment" value="default.png">')) .bindNative('ajax:beforeSend', function() { ok(false, 'ajax:beforeSend should not run') }) .bind('iframe:loading', function() { ok(false, 'form should not get submitted') }) .bindNative('ajax:aborted:file', function() { return false }) .triggerNative('submit') setTimeout(function() { form.find('input[type="file"]').val('') form.unbind('ajax:beforeSend') submit() }, 13) })
}
asyncTest('“ajax:beforeSend” can be observed and stopped with event delegation', 1, function() {
$(document).delegate('form[data-remote]', 'ajax:beforeSend', function() { ok(true, 'ajax:beforeSend observed with event delegation') return false }) submit(function(form) { form.unbind('ajax:send').bindNative('ajax:send', function() { ok(false, 'ajax:send should not run') }) form.unbind('ajax:complete').bindNative('ajax:complete', function() { ok(false, 'ajax:complete should not run') }) $(document).bindNative('ajaxStop', function() { start() }) })
})
asyncTest('“ajax:beforeSend”, “ajax:send”, “ajax:success” and “ajax:complete” are triggered', 9, function() {
submit(function(form) { form.bindNative('ajax:beforeSend', function(e, xhr, settings) { ok(xhr.setRequestHeader, 'first argument to "ajax:beforeSend" should be an XHR object') equal(settings.url, '/echo', 'second argument to "ajax:beforeSend" should be a settings object') }) form.bindNative('ajax:send', function(e, xhr) { ok(xhr.abort, 'first argument to "ajax:send" should be an XHR object') }) form.bindNative('ajax:success', function(e, data, status, xhr) { ok(data.REQUEST_METHOD, 'first argument to ajax:success should be a data object') equal(status, 'OK', 'second argument to ajax:success should be a status string') ok(xhr.getResponseHeader, 'third argument to "ajax:success" should be an XHR object') }) form.bindNative('ajax:complete', function(e, xhr, status) { ok(xhr.getResponseHeader, 'first argument to "ajax:complete" should be an XHR object') equal(status, 'OK', 'second argument to ajax:complete should be a status string') }) })
})
if(window.phantom !== undefined) {
asyncTest('"ajax:beforeSend", "ajax:send", "ajax:error" and "ajax:complete" are triggered on error', 7, function() { submit(function(form) { form.attr('action', '/error') form.bindNative('ajax:beforeSend', function(arg) { ok(true, 'ajax:beforeSend') }) form.bindNative('ajax:send', function(arg) { ok(true, 'ajax:send') }) form.bindNative('ajax:error', function(e, xhr, status, error) { ok(xhr.getResponseHeader, 'first argument to "ajax:error" should be an XHR object') equal(status, 'error', 'second argument to ajax:error should be a status string') // Firefox 8 returns "Forbidden " with trailing space equal($.trim(error), 'Forbidden', 'third argument to ajax:error should be an HTTP status response') // Opera returns "0" for HTTP code equal(xhr.status, window.opera ? 0 : 403, 'status code should be 403') }) }) })
}
// IF THIS TEST IS FAILING, TRY INCREASING THE TIMEOUT AT THE BOTTOM TO > 100 asyncTest('binding to ajax callbacks via .delegate() triggers handlers properly', 4, function() {
$(document) .delegate('form[data-remote]', 'ajax:beforeSend', function() { ok(true, 'ajax:beforeSend handler is triggered') }) .delegate('form[data-remote]', 'ajax:send', function() { ok(true, 'ajax:send handler is triggered') }) .delegate('form[data-remote]', 'ajax:complete', function() { ok(true, 'ajax:complete handler is triggered') }) .delegate('form[data-remote]', 'ajax:success', function() { ok(true, 'ajax:success handler is triggered') }) $('form[data-remote]').triggerNative('submit') setTimeout(function() { start() }, 63)
})
asyncTest('binding to ajax:send event to call jquery methods on ajax object', 2, function() {
$('form[data-remote]') .bindNative('ajax:send', function(e, xhr) { ok(true, 'event should fire') equal(typeof(xhr.abort), 'function', 'event should pass jqXHR object') xhr.abort() }) .triggerNative('submit') setTimeout(function() { start() }, 35)
})
})()