diff options
author | Guillermo Iguaran <guilleiguaran@gmail.com> | 2016-11-25 23:42:29 -0500 |
---|---|---|
committer | Guillermo Iguaran <guilleiguaran@gmail.com> | 2016-11-26 01:23:07 -0500 |
commit | 02568801e6e34ee1ccbb825c6a10dfab83d1663e (patch) | |
tree | 89e472b2f5dcb151f90dd39fe11cc1ad2a97dfeb /actionview/test/ujs/public/test | |
parent | ad3a47759e67a411f3534309cdd704f12f6930a7 (diff) | |
download | rails-02568801e6e34ee1ccbb825c6a10dfab83d1663e.tar.gz rails-02568801e6e34ee1ccbb825c6a10dfab83d1663e.tar.bz2 rails-02568801e6e34ee1ccbb825c6a10dfab83d1663e.zip |
Add UJS tests
Diffstat (limited to 'actionview/test/ujs/public/test')
-rw-r--r-- | actionview/test/ujs/public/test/.eslintrc.yml | 21 | ||||
-rw-r--r-- | actionview/test/ujs/public/test/call-remote-callbacks.js | 469 | ||||
-rw-r--r-- | actionview/test/ujs/public/test/call-remote.js | 247 | ||||
-rw-r--r-- | actionview/test/ujs/public/test/csrf-refresh.js | 24 | ||||
-rw-r--r-- | actionview/test/ujs/public/test/csrf-token.js | 27 | ||||
-rw-r--r-- | actionview/test/ujs/public/test/data-confirm.js | 288 | ||||
-rw-r--r-- | actionview/test/ujs/public/test/data-disable-with.js | 391 | ||||
-rw-r--r-- | actionview/test/ujs/public/test/data-disable.js | 321 | ||||
-rw-r--r-- | actionview/test/ujs/public/test/data-method.js | 85 | ||||
-rw-r--r-- | actionview/test/ujs/public/test/data-remote.js | 400 | ||||
-rw-r--r-- | actionview/test/ujs/public/test/override.js | 56 | ||||
-rw-r--r-- | actionview/test/ujs/public/test/settings.js | 116 |
12 files changed, 2445 insertions, 0 deletions
diff --git a/actionview/test/ujs/public/test/.eslintrc.yml b/actionview/test/ujs/public/test/.eslintrc.yml new file mode 100644 index 0000000000..06d7dd36ea --- /dev/null +++ b/actionview/test/ujs/public/test/.eslintrc.yml @@ -0,0 +1,21 @@ +env: + browser: true +extends: eslint:recommended +rules: + no-undef: off + no-unused-vars: off + indent: off + linebreak-style: ['error', 'unix'] + quotes: ['error', 'single'] + semi: ['error', 'never'] + no-shadow: ['error'] # Prevent potential errors + no-console: 'off' + # styles + space-before-function-paren: ['error', 'never'] + space-before-blocks: 'error' + brace-style: ['error', '1tbs', { allowSingleLine: true }] + key-spacing: 'error' + array-bracket-spacing: 'error' + comma-spacing: 'error' + comma-dangle: 'off' + eol-last: 'error' diff --git a/actionview/test/ujs/public/test/call-remote-callbacks.js b/actionview/test/ujs/public/test/call-remote-callbacks.js new file mode 100644 index 0000000000..082d10bfbd --- /dev/null +++ b/actionview/test/ujs/public/test/call-remote-callbacks.js @@ -0,0 +1,469 @@ +(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() + }) + }) +}) + +asyncTest('blank required form input field should abort request and trigger "ajax:aborted:required" event', 5, function() { + $(document).bind('iframe:loading', function() { + ok(false, 'form should not get submitted') + }) + + var form = $('form[data-remote]') + .append($('<input type="text" name="user_name" required="required">')) + .append($('<textarea name="user_bio" required="required"></textarea>')) + .bindNative('ajax:beforeSend', function() { + ok(false, 'ajax:beforeSend should not run') + }) + .bindNative('ajax:aborted:required', function(e, data) { + data = $(data) + ok(data.length == 2, 'ajax:aborted:required event is passed all blank required inputs (jQuery objects)') + ok(data.first().is('input[name="user_name"]'), 'ajax:aborted:required adds blank required input to data') + ok(data.last().is('textarea[name="user_bio"]'), 'ajax:aborted:required adds blank required textarea to data') + ok(true, 'ajax:aborted:required should run') + }) + .triggerNative('submit') + + setTimeout(function() { + form.find('input[required],textarea[required]').val('Tyler') + form.unbind('ajax:beforeSend') + submit() + }, 13) +}) + +asyncTest('blank required form input for non-remote form should abort normal submission', 1, function() { + var form = $('form[data-remote]') + .append($('<input type="text" name="user_name" required="required">')) + .removeAttr('data-remote') + .bindNative('ujs:everythingStopped', function() { + ok(true, 'ujs:everythingStopped should run') + }) + .triggerNative('submit') + + setTimeout(function() { + start() + }, 13) +}) + +asyncTest('form should be submitted with blank required fields if handler is bound to "ajax:aborted:required" event that returns false', 1, function() { + var form = $('form[data-remote]') + .append($('<input type="text" name="user_name" required="required">')) + .bindNative('ajax:beforeSend', function() { + ok(true, 'ajax:beforeSend should run') + }) + .bindNative('ajax:aborted:required', function() { + return false + }) + .triggerNative('submit') + + setTimeout(function() { + start() + }, 13) +}) + +asyncTest('disabled fields should not be included in blank required check', 2, function() { + var form = $('form[data-remote]') + .append($('<input type="text" name="user_name" required="required" disabled="disabled">')) + .append($('<textarea name="user_bio" required="required" disabled="disabled"></textarea>')) + .bindNative('ajax:beforeSend', function() { + ok(true, 'ajax:beforeSend should run') + }) + .bindNative('ajax:aborted:required', function() { + ok(false, 'ajax:aborted:required should not run') + }) + + submit() +}) + +asyncTest('form should be submitted with blank required fields if it has the "novalidate" attribute', 2, function() { + var form = $('form[data-remote]') + .append($('<input type="text" name="user_name" required="required">')) + .attr('novalidate', 'novalidate') + .bindNative('ajax:beforeSend', function() { + ok(true, 'ajax:beforeSend should run') + }) + .bindNative('ajax:aborted:required', function() { + ok(false, 'ajax:aborted:required should not run') + }) + + submit() +}) + +asyncTest('form should be submitted with blank required fields if the button has the "formnovalidate" attribute', 2, function() { + var submit_button = $('<input type="submit" formnovalidate>') + var form = $('form[data-remote]') + .append($('<input type="text" name="user_name" required="required">')) + .append(submit_button) + .bindNative('ajax:beforeSend', function() { + ok(true, 'ajax:beforeSend should run') + }) + .bindNative('ajax:aborted:required', function() { + ok(false, 'ajax:aborted:required should not run') + }) + + submit_with_button(submit_button) +}) + +asyncTest('blank required form input for non-remote form with "novalidate" attribute should not abort normal submission', 1, function() { + $(document).bind('iframe:loading', function() { + ok(true, 'form should get submitted') + }) + + var form = $('form[data-remote]') + .append($('<input type="text" name="user_name" required="required">')) + .removeAttr('data-remote') + .attr('novalidate', 'novalidate') + .triggerNative('submit') + + setTimeout(function() { + start() + }, 13) +}) + +asyncTest('unchecked required checkbox should abort form submission', 1, function() { + var form = $('form[data-remote]') + .append($('<input type="checkbox" name="agree" required="required">')) + .removeAttr('data-remote') + .bindNative('ujs:everythingStopped', function() { + ok(true, 'ujs:everythingStopped should run') + }) + .triggerNative('submit') + + setTimeout(function() { + start() + }, 13) +}) + +asyncTest('unchecked required radio should abort form submission', 3, function() { + var form = $('form[data-remote]') + .append($('<input type="radio" name="yes_no_none" required="required" value=1>')) + .append($('<input type="radio" name="yes_no_none" required="required" value=2>')) + .removeAttr('data-remote') + .bindNative('ujs:everythingStopped', function() { + ok(true, 'ujs:everythingStopped should run') + }) + .bindNative('ajax:aborted:required', function(e, data) { + data = $(data) + equal(data.length, 2, 'blankRequiredInputs should include both radios') + ok(data.first().is('input[type=radio][value=1]'), 'blankRequiredInputs[0] should be the first radio') + }) + .triggerNative('submit') + + setTimeout(function() { + start() + }, 13) +}) + +asyncTest('required radio should only require one to be checked', 1, function() { + $(document).bind('iframe:loading', function() { + ok(true, 'form should get submitted') + }) + + var form = $('form[data-remote]') + .append($('<input type="radio" name="yes_no" required="required" value=1 id="checkme">')) + .append($('<input type="radio" name="yes_no" required="required" value=2>')) + .removeAttr('data-remote') + .bindNative('ujs:everythingStopped', function() { + ok(false, 'ujs:everythingStopped should not run') + }) + .find('#checkme').prop('checked', true) + .end() + .triggerNative('submit') + + setTimeout(function() { + start() + }, 13) +}) + +asyncTest('required radio should only require one to be checked if not all radios are required', 1, function() { + $(document).bind('iframe:loading', function() { + ok(true, 'form should get submitted') + }) + + var form = $('form[data-remote]') + // Check the radio that is not required + .append($('<input type="radio" name="yes_no_maybe" value=1 >')) + // Check the radio that is not required + .append($('<input type="radio" name="yes_no_maybe" value=2 id="checkme">')) + // Only one needs to be required + .append($('<input type="radio" name="yes_no_maybe" required="required" value=3>')) + .removeAttr('data-remote') + .bindNative('ujs:everythingStopped', function() { + ok(false, 'ujs:everythingStopped should not run') + }) + .find('#checkme').prop('checked', true) + .end() + .triggerNative('submit') + + setTimeout(function() { + start() + }, 13) +}) + +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) +}) + +})() diff --git a/actionview/test/ujs/public/test/call-remote.js b/actionview/test/ujs/public/test/call-remote.js new file mode 100644 index 0000000000..dbeb8ad832 --- /dev/null +++ b/actionview/test/ujs/public/test/call-remote.js @@ -0,0 +1,247 @@ +(function() { + +function buildForm(attrs) { + attrs = $.extend({ action: '/echo', 'data-remote': 'true' }, attrs) + + $('#qunit-fixture').append($('<form />', attrs)) + .find('form').append($('<input type="text" name="user_name" value="john">')) +} + +module('call-remote') + +function submit(fn) { + $('form') + .bindNative('ajax:success', fn) + .bindNative('ajax:complete', function() { start() }) + .triggerNative('submit') +} + +asyncTest('form method is read from "method" and not from "data-method"', 1, function() { + buildForm({ method: 'post', 'data-method': 'get' }) + + submit(function(e, data, status, xhr) { + App.assertPostRequest(data) + }) +}) + +asyncTest('form method is not read from "data-method" attribute in case of missing "method"', 1, function() { + buildForm({ 'data-method': 'put' }) + + submit(function(e, data, status, xhr) { + App.assertGetRequest(data) + }) +}) + +asyncTest('form method is read from submit button "formmethod" if submit is triggered by that button', 1, function() { + var submitButton = $('<input type="submit" formmethod="get">') + buildForm({ method: 'post' }) + + $('#qunit-fixture').find('form').append(submitButton) + .bindNative('ajax:success', function(e, data, status, xhr) { + App.assertGetRequest(data) + }) + .bindNative('ajax:complete', function() { start() }) + + submitButton.triggerNative('click') +}) + +asyncTest('form default method is GET', 1, function() { + buildForm() + + submit(function(e, data, status, xhr) { + App.assertGetRequest(data) + }) +}) + +asyncTest('form url is picked up from "action"', 1, function() { + buildForm({ method: 'post' }) + + submit(function(e, data, status, xhr) { + App.assertRequestPath(data, '/echo') + }) +}) + +asyncTest('form url is read from "action" not "href"', 1, function() { + buildForm({ method: 'post', href: '/echo2' }) + + submit(function(e, data, status, xhr) { + App.assertRequestPath(data, '/echo') + }) +}) + +asyncTest('form url is read from submit button "formaction" if submit is triggered by that button', 1, function() { + var submitButton = $('<input type="submit" formaction="/echo">') + buildForm({ method: 'post', href: '/echo2' }) + + $('#qunit-fixture').find('form').append(submitButton) + .bindNative('ajax:success', function(e, data, status, xhr) { + App.assertRequestPath(data, '/echo') + }) + .bindNative('ajax:complete', function() { start() }) + + submitButton.triggerNative('click') +}) + +asyncTest('prefer JS, but accept any format', 1, function() { + buildForm({ method: 'post' }) + + submit(function(e, data, status, xhr) { + var accept = data.HTTP_ACCEPT + ok(accept.match(/text\/javascript.+\*\/\*/), 'Accept: ' + accept) + }) +}) + +asyncTest('JS code should be executed', 1, function() { + buildForm({ method: 'post', 'data-type': 'script' }) + + $('form').append('<input type="text" name="content_type" value="text/javascript">') + $('form').append('<input type="text" name="content" value="ok(true, \'remote code should be run\')">') + + submit() +}) + +asyncTest('XML document should be parsed', 1, function() { + buildForm({ method: 'post', 'data-type': 'html' }) + + $('form').append('<input type="text" name="content_type" value="application/xml">') + $('form').append('<input type="text" name="content" value="<p>hello</p>">') + + submit(function(e, data, status, xhr) { + ok(data instanceof Document, 'returned data should be an XML document') + }) +}) + +asyncTest('accept application/json if "data-type" is json', 1, function() { + buildForm({ method: 'post', 'data-type': 'json' }) + + submit(function(e, data, status, xhr) { + equal(data.HTTP_ACCEPT, 'application/json, text/javascript, */*; q=0.01') + }) +}) + +asyncTest('allow empty "data-remote" attribute', 1, function() { + var form = $('#qunit-fixture').append($('<form action="/echo" data-remote />')).find('form') + + submit(function() { + ok(true, 'form with empty "data-remote" attribute is also allowed') + }) +}) + +asyncTest('query string in form action should be stripped in a GET request in normal submit', 1, function() { + buildForm({ action: '/echo?param1=abc', 'data-remote': 'false' }) + + $(document).one('iframe:loaded', function(e, data) { + equal(data.params.param1, undefined, '"param1" should not be passed to server') + start() + }) + + $('#qunit-fixture form').triggerNative('submit') +}) + +asyncTest('query string in form action should be stripped in a GET request in ajax submit', 1, function() { + buildForm({ action: '/echo?param1=abc' }) + + submit(function(e, data, status, xhr) { + equal(data.params.param1, undefined, '"param1" should not be passed to server') + }) +}) + +asyncTest('query string in form action should not be stripped in a POST request in normal submit', 1, function() { + buildForm({ action: '/echo?param1=abc', method: 'post', 'data-remote': 'false' }) + + $(document).one('iframe:loaded', function(e, data) { + equal(data.params.param1, 'abc', '"param1" should be passed to server') + start() + }) + + $('#qunit-fixture form').triggerNative('submit') +}) + +asyncTest('query string in form action should not be stripped in a POST request in ajax submit', 1, function() { + buildForm({ action: '/echo?param1=abc', method: 'post' }) + + submit(function(e, data, status, xhr) { + equal(data.params.param1, 'abc', '"param1" should be passed to server') + }) +}) + +asyncTest('allow empty form "action"', 1, function() { + var currentLocation, ajaxLocation + + buildForm({ action: '' }) + + $('#qunit-fixture').find('form') + .bindNative('ajax:beforeSend', function(e, xhr, settings) { + // Get current location (the same way jQuery does) + try { + currentLocation = location.href + } catch(err) { + currentLocation = document.createElement( 'a' ) + currentLocation.href = '' + currentLocation = currentLocation.href + } + currentLocation = currentLocation.replace(/\?.*$/, '') + + // Actual location (strip out settings.data that jQuery serializes and appends) + // HACK: can no longer use settings.data below to see what was appended to URL, as of + // jQuery 1.6.3 (see http://bugs.jquery.com/ticket/10202 and https://github.com/jquery/jquery/pull/544) + ajaxLocation = settings.url.replace('user_name=john', '').replace(/&$/, '').replace(/\?$/, '') + equal(ajaxLocation.match(/^(.*)/)[1], currentLocation, 'URL should be current page by default') + + // Prevent the request from actually getting sent to the current page and + // causing an error. + return false + }) + .triggerNative('submit') + + setTimeout(function() { start() }, 13) +}) + +asyncTest('sends CSRF token in custom header', 1, function() { + buildForm({ method: 'post' }) + $('#qunit-fixture').append('<meta name="csrf-token" content="cf50faa3fe97702ca1ae" />') + + submit(function(e, data, status, xhr) { + equal(data.HTTP_X_CSRF_TOKEN, 'cf50faa3fe97702ca1ae', 'X-CSRF-Token header should be sent') + }) +}) + +asyncTest('intelligently guesses crossDomain behavior when target URL has a different protocol and/or hostname', 1, function() { + + // Don't set data-cross-domain here, just set action to be a different domain than localhost + buildForm({ action: 'http://www.alfajango.com' }) + $('#qunit-fixture').append('<meta name="csrf-token" content="cf50faa3fe97702ca1ae" />') + + $('#qunit-fixture').find('form') + .bindNative('ajax:beforeSend', function(evt, req, settings) { + + equal(settings.crossDomain, true, 'crossDomain should be set to true') + + // prevent request from actually getting sent off-domain + return false + }) + .triggerNative('submit') + + setTimeout(function() { start() }, 13) +}) + +asyncTest('intelligently guesses crossDomain behavior when target URL consists of only a path', 1, function() { + + // Don't set data-cross-domain here, just set action to be a different domain than localhost + buildForm({ action: '/just/a/path' }) + $('#qunit-fixture').append('<meta name="csrf-token" content="cf50faa3fe97702ca1ae" />') + + $('#qunit-fixture').find('form') + .bindNative('ajax:beforeSend', function(evt, req, settings) { + + equal(settings.crossDomain, false, 'crossDomain should be set to false') + + // prevent request from actually getting sent off-domain + return false + }) + .triggerNative('submit') + + setTimeout(function() { start() }, 13) +}) + +})() diff --git a/actionview/test/ujs/public/test/csrf-refresh.js b/actionview/test/ujs/public/test/csrf-refresh.js new file mode 100644 index 0000000000..e302042542 --- /dev/null +++ b/actionview/test/ujs/public/test/csrf-refresh.js @@ -0,0 +1,24 @@ +(function() { + +module('csrf-refresh', {}) + +asyncTest('refresh all csrf tokens', 1, function() { + var correctToken = 'cf50faa3fe97702ca1ae' + + var form = $('<form />') + var input = $('<input>').attr({ type: 'hidden', name: 'authenticity_token', id: 'authenticity_token', value: 'foo' }) + input.appendTo(form) + + $('#qunit-fixture') + .append('<meta name="csrf-param" content="authenticity_token"/>') + .append('<meta name="csrf-token" content="' + correctToken + '"/>') + .append(form) + + $.rails.refreshCSRFTokens() + currentToken = $('#qunit-fixture #authenticity_token').val() + + start() + equal(currentToken, correctToken) +}) + +})() diff --git a/actionview/test/ujs/public/test/csrf-token.js b/actionview/test/ujs/public/test/csrf-token.js new file mode 100644 index 0000000000..388b40e057 --- /dev/null +++ b/actionview/test/ujs/public/test/csrf-token.js @@ -0,0 +1,27 @@ +(function() { + +module('csrf-token', {}) + +asyncTest('find csrf token', 1, function() { + var correctToken = 'cf50faa3fe97702ca1ae' + + $('#qunit-fixture').append('<meta name="csrf-token" content="' + correctToken + '"/>') + + currentToken = $.rails.csrfToken() + + start() + equal(currentToken, correctToken) +}) + +asyncTest('find csrf param', 1, function() { + var correctParam = 'authenticity_token' + + $('#qunit-fixture').append('<meta name="csrf-param" content="' + correctParam + '"/>') + + currentParam = $.rails.csrfParam() + + start() + equal(currentParam, correctParam) +}) + +})() diff --git a/actionview/test/ujs/public/test/data-confirm.js b/actionview/test/ujs/public/test/data-confirm.js new file mode 100644 index 0000000000..28190c2250 --- /dev/null +++ b/actionview/test/ujs/public/test/data-confirm.js @@ -0,0 +1,288 @@ +module('data-confirm', { + setup: function() { + $('#qunit-fixture').append($('<a />', { + href: '/echo', + 'data-remote': 'true', + 'data-confirm': 'Are you absolutely sure?', + text: 'my social security number' + })) + + $('#qunit-fixture').append($('<button />', { + 'data-url': '/echo', + 'data-remote': 'true', + 'data-confirm': 'Are you absolutely sure?', + text: 'Click me' + })) + + $('#qunit-fixture').append($('<form />', { + id: 'confirm', + action: '/echo', + 'data-remote': 'true' + })) + + $('#qunit-fixture').append($('<input />', { + type: 'submit', + form: 'confirm', + 'data-confirm': 'Are you absolutely sure?' + })) + + this.windowConfirm = window.confirm + }, + teardown: function() { + window.confirm = this.windowConfirm + } +}) + +asyncTest('clicking on a link with data-confirm attribute. Confirm yes.', 6, function() { + var message + // auto-confirm: + window.confirm = function(msg) { message = msg; return true } + + $('a[data-confirm]') + .bindNative('confirm:complete', function(e, data) { + App.assertCallbackInvoked('confirm:complete') + ok(data == true, 'confirm:complete passes in confirm answer (true)') + }) + .bindNative('ajax:success', function(e, data, status, xhr) { + App.assertCallbackInvoked('ajax:success') + App.assertRequestPath(data, '/echo') + App.assertGetRequest(data) + + equal(message, 'Are you absolutely sure?') + start() + }) + .triggerNative('click') +}) + +asyncTest('clicking on a button with data-confirm attribute. Confirm yes.', 6, function() { + var message + // auto-confirm: + window.confirm = function(msg) { message = msg; return true } + + $('button[data-confirm]') + .bindNative('confirm:complete', function(e, data) { + App.assertCallbackInvoked('confirm:complete') + ok(data == true, 'confirm:complete passes in confirm answer (true)') + }) + .bindNative('ajax:success', function(e, data, status, xhr) { + App.assertCallbackInvoked('ajax:success') + App.assertRequestPath(data, '/echo') + App.assertGetRequest(data) + + equal(message, 'Are you absolutely sure?') + start() + }) + .triggerNative('click') +}) + +asyncTest('clicking on a link with data-confirm attribute. Confirm No.', 3, function() { + var message + // auto-decline: + window.confirm = function(msg) { message = msg; return false } + + $('a[data-confirm]') + .bindNative('confirm:complete', function(e, data) { + App.assertCallbackInvoked('confirm:complete') + ok(data == false, 'confirm:complete passes in confirm answer (false)') + }) + .bindNative('ajax:beforeSend', function(e, data, status, xhr) { + App.assertCallbackNotInvoked('ajax:beforeSend') + }) + .triggerNative('click') + + setTimeout(function() { + equal(message, 'Are you absolutely sure?') + start() + }, 50) +}) + +asyncTest('clicking on a button with data-confirm attribute. Confirm No.', 3, function() { + var message + // auto-decline: + window.confirm = function(msg) { message = msg; return false } + + $('button[data-confirm]') + .bindNative('confirm:complete', function(e, data) { + App.assertCallbackInvoked('confirm:complete') + ok(data == false, 'confirm:complete passes in confirm answer (false)') + }) + .bindNative('ajax:beforeSend', function(e, data, status, xhr) { + App.assertCallbackNotInvoked('ajax:beforeSend') + }) + .triggerNative('click') + + setTimeout(function() { + equal(message, 'Are you absolutely sure?') + start() + }, 50) +}) + +asyncTest('clicking on a button with data-confirm attribute. Confirm error.', 3, function() { + var message + // auto-decline: + window.confirm = function(msg) { message = msg; throw 'some random error' } + + $('button[data-confirm]') + .bindNative('confirm:complete', function(e, data) { + App.assertCallbackInvoked('confirm:complete') + ok(data == false, 'confirm:complete passes in confirm answer (false)') + }) + .bindNative('ajax:beforeSend', function(e, data, status, xhr) { + App.assertCallbackNotInvoked('ajax:beforeSend') + }) + .triggerNative('click') + + setTimeout(function() { + equal(message, 'Are you absolutely sure?') + start() + }, 50) +}) + +asyncTest('clicking on a submit button with form and data-confirm attributes. Confirm No.', 3, function() { + var message + // auto-decline: + window.confirm = function(msg) { message = msg; return false } + + $('input[type=submit][form]') + .bindNative('confirm:complete', function(e, data) { + App.assertCallbackInvoked('confirm:complete') + ok(data == false, 'confirm:complete passes in confirm answer (false)') + }) + .bindNative('ajax:beforeSend', function(e, data, status, xhr) { + App.assertCallbackNotInvoked('ajax:beforeSend') + }) + .triggerNative('click') + + setTimeout(function() { + equal(message, 'Are you absolutely sure?') + start() + }, 50) +}) + +asyncTest('binding to confirm event of a link and returning false', 1, function() { + // redefine confirm function so we can make sure it's not called + window.confirm = function(msg) { + ok(false, 'confirm dialog should not be called') + } + + $('a[data-confirm]') + .bindNative('confirm', function() { + App.assertCallbackInvoked('confirm') + return false + }) + .bindNative('confirm:complete', function() { + App.assertCallbackNotInvoked('confirm:complete') + }) + .triggerNative('click') + + setTimeout(function() { + start() + }, 50) +}) + +asyncTest('binding to confirm event of a button and returning false', 1, function() { + // redefine confirm function so we can make sure it's not called + window.confirm = function(msg) { + ok(false, 'confirm dialog should not be called') + } + + $('button[data-confirm]') + .bindNative('confirm', function() { + App.assertCallbackInvoked('confirm') + return false + }) + .bindNative('confirm:complete', function() { + App.assertCallbackNotInvoked('confirm:complete') + }) + .triggerNative('click') + + setTimeout(function() { + start() + }, 50) +}) + +asyncTest('binding to confirm:complete event of a link and returning false', 2, function() { + // auto-confirm: + window.confirm = function(msg) { + ok(true, 'confirm dialog should be called') + return true + } + + $('a[data-confirm]') + .bindNative('confirm:complete', function() { + App.assertCallbackInvoked('confirm:complete') + return false + }) + .bindNative('ajax:beforeSend', function() { + App.assertCallbackNotInvoked('ajax:beforeSend') + }) + .triggerNative('click') + + setTimeout(function() { + start() + }, 50) +}) + +asyncTest('binding to confirm:complete event of a button and returning false', 2, function() { + // auto-confirm: + window.confirm = function(msg) { + ok(true, 'confirm dialog should be called') + return true + } + + $('button[data-confirm]') + .bindNative('confirm:complete', function() { + App.assertCallbackInvoked('confirm:complete') + return false + }) + .bindNative('ajax:beforeSend', function() { + App.assertCallbackNotInvoked('ajax:beforeSend') + }) + .triggerNative('click') + + setTimeout(function() { + start() + }, 50) +}) + +asyncTest('a button inside a form only confirms once', 1, function() { + var confirmations = 0 + window.confirm = function(msg) { + confirmations++ + return true + } + + $('#qunit-fixture').append($('<form />').append($('<button />', { + 'data-remote': 'true', + 'data-confirm': 'Are you absolutely sure?', + text: 'Click me' + }))) + + $('form > button[data-confirm]').triggerNative('click') + + ok(confirmations === 1, 'confirmation counter should be 1, but it was ' + confirmations) + start() +}) + +asyncTest('clicking on the children of a link should also trigger a confirm', 6, function() { + var message + // auto-confirm: + window.confirm = function(msg) { message = msg; return true } + + $('a[data-confirm]') + .html('<strong>Click me</strong>') + .bindNative('confirm:complete', function(e, data) { + App.assertCallbackInvoked('confirm:complete') + ok(data == true, 'confirm:complete passes in confirm answer (true)') + }) + .bindNative('ajax:success', function(e, data, status, xhr) { + App.assertCallbackInvoked('ajax:success') + App.assertRequestPath(data, '/echo') + App.assertGetRequest(data) + + equal(message, 'Are you absolutely sure?') + start() + }) + .find('strong') + .triggerNative('click') +}) diff --git a/actionview/test/ujs/public/test/data-disable-with.js b/actionview/test/ujs/public/test/data-disable-with.js new file mode 100644 index 0000000000..b29cbbc867 --- /dev/null +++ b/actionview/test/ujs/public/test/data-disable-with.js @@ -0,0 +1,391 @@ +module('data-disable-with', { + setup: function() { + $('#qunit-fixture').append($('<form />', { + action: '/echo', + 'data-remote': 'true', + method: 'post' + })) + .find('form') + .append($('<input type="text" data-disable-with="processing ..." name="user_name" value="john" />')) + + $('#qunit-fixture').append($('<form />', { + action: '/echo', + method: 'post', + id: 'not_remote' + })) + .find('form:last') + // WEEIRDD: the form won't submit to an iframe if the button is name="submit" (??!) + .append($('<input type="submit" data-disable-with="submitting ..." name="submit2" value="Submit" />')) + + $('#qunit-fixture').append($('<a />', { + text: 'Click me', + href: '/echo', + 'data-disable-with': 'clicking...' + })) + + $('#qunit-fixture').append($('<input />', { + type: 'submit', + form: 'not_remote', + 'data-disable-with': 'form attr submitting', + name: 'submit3', + value: 'Form Attr Submit' + })) + + $('#qunit-fixture').append($('<button />', { + text: 'Click me', + 'data-remote': true, + 'data-url': '/echo', + 'data-disable-with': 'clicking...' + })) + }, + teardown: function() { + $(document).unbind('iframe:loaded') + } +}) + +asyncTest('form input field with "data-disable-with" attribute', 7, function() { + var form = $('form[data-remote]'), input = form.find('input[type=text]') + + App.checkEnabledState(input, 'john') + + form.bindNative('ajax:success', function(e, data) { + setTimeout(function() { + App.checkEnabledState(input, 'john') + equal(data.params.user_name, 'john') + start() + }, 13) + }) + form.triggerNative('submit') + + App.checkDisabledState(input, 'processing ...') +}) + +asyncTest('blank form input field with "data-disable-with" attribute', 7, function() { + var form = $('form[data-remote]'), input = form.find('input[type=text]') + + input.val('') + App.checkEnabledState(input, '') + + form.bindNative('ajax:success', function(e, data) { + setTimeout(function() { + App.checkEnabledState(input, '') + equal(data.params.user_name, '') + start() + }, 13) + }) + form.triggerNative('submit') + + App.checkDisabledState(input, 'processing ...') +}) + +asyncTest('form button with "data-disable-with" attribute', 6, function() { + var form = $('form[data-remote]'), button = $('<button data-disable-with="submitting ..." name="submit2">Submit</button>') + form.append(button) + + App.checkEnabledState(button, 'Submit') + + form.bindNative('ajax:success', function(e, data) { + setTimeout(function() { + App.checkEnabledState(button, 'Submit') + start() + }, 13) + }) + form.triggerNative('submit') + + App.checkDisabledState(button, 'submitting ...') +}) + +asyncTest('form input[type=submit][data-disable-with] disables', 6, function() { + var form = $('form:not([data-remote])'), input = form.find('input[type=submit]') + + App.checkEnabledState(input, 'Submit') + + $(document).bind('iframe:loaded', function(e, data) { + setTimeout(function() { + App.checkDisabledState(input, 'submitting ...') + start() + }, 30) + }) + form.triggerNative('submit') + + setTimeout(function() { + App.checkDisabledState(input, 'submitting ...') + }, 30) +}) + +test('form input[type=submit][data-disable-with] re-enables when `pageshow` event is triggered', function() { + var form = $('form:not([data-remote])'), input = form.find('input[type=submit]') + + App.checkEnabledState(input, 'Submit') + + // Emulate the disabled state without submitting the form at all, what is the + // state after going back on firefox after submitting a form. + // + // See https://github.com/rails/jquery-ujs/issues/357 + $.rails.disableElement(form[0]) + + App.checkDisabledState(input, 'submitting ...') + + $(window).triggerNative('pageshow') + + App.checkEnabledState(input, 'Submit') +}) + +asyncTest('form[data-remote] input[type=submit][data-disable-with] is replaced in ajax callback', 2, function() { + var form = $('form:not([data-remote])').attr('data-remote', 'true'), origFormContents = form.html() + + form.bindNative('ajax:success', function() { + form.html(origFormContents) + + setTimeout(function() { + var input = form.find('input[type=submit]') + App.checkEnabledState(input, 'Submit') + start() + }, 30) + }).triggerNative('submit') +}) + +asyncTest('form[data-remote] input[data-disable-with] is replaced with disabled field in ajax callback', 2, function() { + var form = $('form:not([data-remote])').attr('data-remote', 'true'), input = form.find('input[type=submit]'), + newDisabledInput = input.clone().attr('disabled', 'disabled') + + form.bindNative('ajax:success', function() { + input.replaceWith(newDisabledInput) + + setTimeout(function() { + App.checkEnabledState(newDisabledInput, 'Submit') + start() + }, 30) + }).triggerNative('submit') +}) + +asyncTest('form input[type=submit][data-disable-with] using "form" attribute disables', 6, function() { + var form = $('#not_remote'), input = $('input[form=not_remote]') + App.checkEnabledState(input, 'Form Attr Submit') + + $(document).bind('iframe:loaded', function(e, data) { + setTimeout(function() { + App.checkDisabledState(input, 'form attr submitting') + start() + }, 30) + }) + form.triggerNative('submit') + + setTimeout(function() { + App.checkDisabledState(input, 'form attr submitting') + }, 30) + +}) + +asyncTest('form[data-remote] textarea[data-disable-with] attribute', 3, function() { + var form = $('form[data-remote]'), + textarea = $('<textarea data-disable-with="processing ..." name="user_bio">born, lived, died.</textarea>').appendTo(form) + + form.bindNative('ajax:success', function(e, data) { + setTimeout(function() { + equal(data.params.user_bio, 'born, lived, died.') + start() + }, 13) + }) + form.triggerNative('submit') + + App.checkDisabledState(textarea, 'processing ...') +}) + +asyncTest('a[data-disable-with] disables', 4, function() { + var link = $('a[data-disable-with]') + + App.checkEnabledState(link, 'Click me') + + link.triggerNative('click') + App.checkDisabledState(link, 'clicking...') + start() +}) + +test('a[data-disable-with] re-enables when `pageshow` event is triggered', function() { + var link = $('a[data-disable-with]') + + App.checkEnabledState(link, 'Click me') + + link.triggerNative('click') + App.checkDisabledState(link, 'clicking...') + + $(window).triggerNative('pageshow') + App.checkEnabledState(link, 'Click me') +}) + +asyncTest('a[data-remote][data-disable-with] disables and re-enables', 6, function() { + var link = $('a[data-disable-with]').attr('data-remote', true) + + App.checkEnabledState(link, 'Click me') + + link + .bindNative('ajax:beforeSend', function() { + App.checkDisabledState(link, 'clicking...') + }) + .bindNative('ajax:complete', function() { + setTimeout( function() { + App.checkEnabledState(link, 'Click me') + start() + }, 15) + }) + .triggerNative('click') +}) + +asyncTest('a[data-remote][data-disable-with] re-enables when `ajax:before` event is cancelled', 6, function() { + var link = $('a[data-disable-with]').attr('data-remote', true) + + App.checkEnabledState(link, 'Click me') + + link + .bindNative('ajax:before', function() { + App.checkDisabledState(link, 'clicking...') + return false + }) + .triggerNative('click') + + setTimeout(function() { + App.checkEnabledState(link, 'Click me') + start() + }, 30) +}) + +asyncTest('a[data-remote][data-disable-with] re-enables when `ajax:beforeSend` event is cancelled', 6, function() { + var link = $('a[data-disable-with]').attr('data-remote', true) + + App.checkEnabledState(link, 'Click me') + + link + .bindNative('ajax:beforeSend', function() { + App.checkDisabledState(link, 'clicking...') + return false + }) + .triggerNative('click') + + setTimeout(function() { + App.checkEnabledState(link, 'Click me') + start() + }, 30) +}) + +asyncTest('a[data-remote][data-disable-with] re-enables when `ajax:error` event is triggered', 6, function() { + var link = $('a[data-disable-with]').attr('data-remote', true).attr('href', '/error') + + App.checkEnabledState(link, 'Click me') + + link + .bindNative('ajax:beforeSend', function() { + App.checkDisabledState(link, 'clicking...') + }) + .triggerNative('click') + + setTimeout(function() { + App.checkEnabledState(link, 'Click me') + start() + }, 30) +}) + +asyncTest('form[data-remote] input|button|textarea[data-disable-with] does not disable when `ajax:beforeSend` event is cancelled', 8, function() { + var form = $('form[data-remote]'), + input = form.find('input:text'), + button = $('<button data-disable-with="submitting ..." name="submit2">Submit</button>').appendTo(form), + textarea = $('<textarea data-disable-with="processing ..." name="user_bio">born, lived, died.</textarea>').appendTo(form), + submit = $('<input type="submit" data-disable-with="submitting ..." name="submit2" value="Submit" />').appendTo(form) + + form + .bindNative('ajax:beforeSend', function() { + return false + }) + .triggerNative('submit') + + App.checkEnabledState(input, 'john') + App.checkEnabledState(button, 'Submit') + App.checkEnabledState(textarea, 'born, lived, died.') + App.checkEnabledState(submit, 'Submit') + + start() +}) + +asyncTest('ctrl-clicking on a link does not disables the link', 6, function() { + var link = $('a[data-disable-with]') + + App.checkEnabledState(link, 'Click me') + + link.triggerNative('click', { metaKey: true }) + App.checkEnabledState(link, 'Click me') + + link.triggerNative('click', { metaKey: true }) + App.checkEnabledState(link, 'Click me') + start() +}) + +asyncTest('button[data-remote][data-disable-with] disables and re-enables', 6, function() { + var button = $('button[data-remote][data-disable-with]') + + App.checkEnabledState(button, 'Click me') + + button + .bindNative('ajax:send', function() { + App.checkDisabledState(button, 'clicking...') + }) + .bindNative('ajax:complete', function() { + setTimeout( function() { + App.checkEnabledState(button, 'Click me') + start() + }, 15) + }) + .triggerNative('click') +}) + +asyncTest('button[data-remote][data-disable-with] re-enables when `ajax:before` event is cancelled', 6, function() { + var button = $('button[data-remote][data-disable-with]') + + App.checkEnabledState(button, 'Click me') + + button + .bindNative('ajax:before', function() { + App.checkDisabledState(button, 'clicking...') + return false + }) + .triggerNative('click') + + setTimeout(function() { + App.checkEnabledState(button, 'Click me') + start() + }, 30) +}) + +asyncTest('button[data-remote][data-disable-with] re-enables when `ajax:beforeSend` event is cancelled', 6, function() { + var button = $('button[data-remote][data-disable-with]') + + App.checkEnabledState(button, 'Click me') + + button + .bindNative('ajax:beforeSend', function() { + App.checkDisabledState(button, 'clicking...') + return false + }) + .triggerNative('click') + + setTimeout(function() { + App.checkEnabledState(button, 'Click me') + start() + }, 30) +}) + +asyncTest('button[data-remote][data-disable-with] re-enables when `ajax:error` event is triggered', 6, function() { + var button = $('a[data-disable-with]').attr('data-remote', true).attr('href', '/error') + + App.checkEnabledState(button, 'Click me') + + button + .bindNative('ajax:send', function() { + App.checkDisabledState(button, 'clicking...') + }) + .triggerNative('click') + + setTimeout(function() { + App.checkEnabledState(button, 'Click me') + start() + }, 30) +}) diff --git a/actionview/test/ujs/public/test/data-disable.js b/actionview/test/ujs/public/test/data-disable.js new file mode 100644 index 0000000000..ccc38cf9ae --- /dev/null +++ b/actionview/test/ujs/public/test/data-disable.js @@ -0,0 +1,321 @@ +module('data-disable', { + setup: function() { + $('#qunit-fixture').append($('<form />', { + action: '/echo', + 'data-remote': 'true', + method: 'post' + })) + .find('form') + .append($('<input type="text" data-disable name="user_name" value="john" />')) + + $('#qunit-fixture').append($('<form />', { + action: '/echo', + method: 'post' + })) + .find('form:last') + // WEEIRDD: the form won't submit to an iframe if the button is name="submit" (??!) + .append($('<input type="submit" data-disable name="submit2" value="Submit" />')) + + $('#qunit-fixture').append($('<a />', { + text: 'Click me', + href: '/echo', + 'data-disable': 'true' + })) + + $('#qunit-fixture').append($('<button />', { + text: 'Click me', + 'data-remote': true, + 'data-url': '/echo', + 'data-disable': 'true' + })) + }, + teardown: function() { + $(document).unbind('iframe:loaded') + } +}) + +asyncTest('form input field with "data-disable" attribute', 7, function() { + var form = $('form[data-remote]'), input = form.find('input[type=text]') + + App.checkEnabledState(input, 'john') + + form.bindNative('ajax:success', function(e, data) { + setTimeout(function() { + App.checkEnabledState(input, 'john') + equal(data.params.user_name, 'john') + start() + }, 13) + }) + form.triggerNative('submit') + + App.checkDisabledState(input, 'john') +}) + +asyncTest('form button with "data-disable" attribute', 7, function() { + var form = $('form[data-remote]'), button = $('<button data-disable name="submit2">Submit</button>') + form.append(button) + + App.checkEnabledState(button, 'Submit') + + form.bindNative('ajax:success', function(e, data) { + setTimeout(function() { + App.checkEnabledState(button, 'Submit') + start() + }, 13) + }) + form.triggerNative('submit') + + App.checkDisabledState(button, 'Submit') + equal(button.data('ujs:enable-with'), undefined) +}) + +asyncTest('form input[type=submit][data-disable] disables', 6, function() { + var form = $('form:not([data-remote])'), input = form.find('input[type=submit]') + + App.checkEnabledState(input, 'Submit') + + // WEEIRDD: attaching this handler makes the test work in IE7 + $(document).bind('iframe:loading', function(e, f) {}) + + $(document).bind('iframe:loaded', function(e, data) { + setTimeout(function() { + App.checkDisabledState(input, 'Submit') + start() + }, 30) + }) + form.triggerNative('submit') + + setTimeout(function() { + App.checkDisabledState(input, 'Submit') + }, 30) +}) + +asyncTest('form[data-remote] input[type=submit][data-disable] is replaced in ajax callback', 2, function() { + var form = $('form:not([data-remote])').attr('data-remote', 'true'), origFormContents = form.html() + + form.bindNative('ajax:success', function() { + form.html(origFormContents) + + setTimeout(function() { + var input = form.find('input[type=submit]') + App.checkEnabledState(input, 'Submit') + start() + }, 30) + }).triggerNative('submit') +}) + +asyncTest('form[data-remote] input[data-disable] is replaced with disabled field in ajax callback', 2, function() { + var form = $('form:not([data-remote])').attr('data-remote', 'true'), input = form.find('input[type=submit]'), + newDisabledInput = input.clone().attr('disabled', 'disabled') + + form.bindNative('ajax:success', function() { + input.replaceWith(newDisabledInput) + + setTimeout(function() { + App.checkEnabledState(newDisabledInput, 'Submit') + start() + }, 30) + }).triggerNative('submit') +}) + +asyncTest('form[data-remote] textarea[data-disable] attribute', 3, function() { + var form = $('form[data-remote]'), + textarea = $('<textarea data-disable name="user_bio">born, lived, died.</textarea>').appendTo(form) + + form.bindNative('ajax:success', function(e, data) { + setTimeout(function() { + equal(data.params.user_bio, 'born, lived, died.') + start() + }, 13) + }) + form.triggerNative('submit') + + App.checkDisabledState(textarea, 'born, lived, died.') +}) + +asyncTest('a[data-disable] disables', 5, function() { + var link = $('a[data-disable]') + + App.checkEnabledState(link, 'Click me') + + link.triggerNative('click') + App.checkDisabledState(link, 'Click me') + equal(link.data('ujs:enable-with'), undefined) + start() +}) + +asyncTest('a[data-remote][data-disable] disables and re-enables', 6, function() { + var link = $('a[data-disable]').attr('data-remote', true) + + App.checkEnabledState(link, 'Click me') + + link + .bindNative('ajax:send', function() { + App.checkDisabledState(link, 'Click me') + }) + .bindNative('ajax:complete', function() { + setTimeout( function() { + App.checkEnabledState(link, 'Click me') + start() + }, 15) + }) + .triggerNative('click') +}) + +asyncTest('a[data-remote][data-disable] re-enables when `ajax:before` event is cancelled', 6, function() { + var link = $('a[data-disable]').attr('data-remote', true) + + App.checkEnabledState(link, 'Click me') + + link + .bindNative('ajax:before', function() { + App.checkDisabledState(link, 'Click me') + return false + }) + .triggerNative('click') + + setTimeout(function() { + App.checkEnabledState(link, 'Click me') + start() + }, 30) +}) + +asyncTest('a[data-remote][data-disable] re-enables when `ajax:beforeSend` event is cancelled', 6, function() { + var link = $('a[data-disable]').attr('data-remote', true) + + App.checkEnabledState(link, 'Click me') + + link + .bindNative('ajax:beforeSend', function() { + App.checkDisabledState(link, 'Click me') + return false + }) + .triggerNative('click') + + setTimeout(function() { + App.checkEnabledState(link, 'Click me') + start() + }, 30) +}) + +asyncTest('a[data-remote][data-disable] re-enables when `ajax:error` event is triggered', 6, function() { + var link = $('a[data-disable]').attr('data-remote', true).attr('href', '/error') + + App.checkEnabledState(link, 'Click me') + + link + .bindNative('ajax:send', function() { + App.checkDisabledState(link, 'Click me') + }) + .triggerNative('click') + + setTimeout(function() { + App.checkEnabledState(link, 'Click me') + start() + }, 30) +}) + +asyncTest('form[data-remote] input|button|textarea[data-disable] does not disable when `ajax:beforeSend` event is cancelled', 8, function() { + var form = $('form[data-remote]'), + input = form.find('input:text'), + button = $('<button data-disable="submitting ..." name="submit2">Submit</button>').appendTo(form), + textarea = $('<textarea data-disable name="user_bio">born, lived, died.</textarea>').appendTo(form), + submit = $('<input type="submit" data-disable="submitting ..." name="submit2" value="Submit" />').appendTo(form) + + form + .bindNative('ajax:beforeSend', function() { + return false + }) + .triggerNative('submit') + + App.checkEnabledState(input, 'john') + App.checkEnabledState(button, 'Submit') + App.checkEnabledState(textarea, 'born, lived, died.') + App.checkEnabledState(submit, 'Submit') + + start() +}) + +asyncTest('ctrl-clicking on a link does not disables the link', 6, function() { + var link = $('a[data-disable]') + + App.checkEnabledState(link, 'Click me') + + link.triggerNative('click', { metaKey: true }) + App.checkEnabledState(link, 'Click me') + + link.triggerNative('click', { ctrlKey: true }) + App.checkEnabledState(link, 'Click me') + start() +}) + +asyncTest('button[data-remote][data-disable] disables and re-enables', 6, function() { + var button = $('button[data-remote][data-disable]') + + App.checkEnabledState(button, 'Click me') + + button + .bindNative('ajax:send', function() { + App.checkDisabledState(button, 'Click me') + }) + .bindNative('ajax:complete', function() { + setTimeout( function() { + App.checkEnabledState(button, 'Click me') + start() + }, 15) + }) + .triggerNative('click') +}) + +asyncTest('button[data-remote][data-disable] re-enables when `ajax:before` event is cancelled', 6, function() { + var button = $('button[data-remote][data-disable]') + + App.checkEnabledState(button, 'Click me') + + button + .bindNative('ajax:before', function() { + App.checkDisabledState(button, 'Click me') + return false + }) + .triggerNative('click') + + setTimeout(function() { + App.checkEnabledState(button, 'Click me') + start() + }, 30) +}) + +asyncTest('button[data-remote][data-disable] re-enables when `ajax:beforeSend` event is cancelled', 6, function() { + var button = $('button[data-remote][data-disable]') + + App.checkEnabledState(button, 'Click me') + + button + .bindNative('ajax:beforeSend', function() { + App.checkDisabledState(button, 'Click me') + return false + }) + .triggerNative('click') + + setTimeout(function() { + App.checkEnabledState(button, 'Click me') + start() + }, 30) +}) + +asyncTest('button[data-remote][data-disable] re-enables when `ajax:error` event is triggered', 6, function() { + var button = $('a[data-disable]').attr('data-remote', true).attr('href', '/error') + + App.checkEnabledState(button, 'Click me') + + button + .bindNative('ajax:send', function() { + App.checkDisabledState(button, 'Click me') + }) + .triggerNative('click') + + setTimeout(function() { + App.checkEnabledState(button, 'Click me') + start() + }, 30) +}) diff --git a/actionview/test/ujs/public/test/data-method.js b/actionview/test/ujs/public/test/data-method.js new file mode 100644 index 0000000000..47d940c577 --- /dev/null +++ b/actionview/test/ujs/public/test/data-method.js @@ -0,0 +1,85 @@ +(function() { + +module('data-method', { + setup: function() { + $('#qunit-fixture').append($('<a />', { + href: '/echo', 'data-method': 'delete', text: 'destroy!' + })) + }, + teardown: function() { + $(document).unbind('iframe:loaded') + } +}) + +function submit(fn, options) { + $(document).bind('iframe:loaded', function(e, data) { + fn(data) + start() + }) + + $('#qunit-fixture').find('a') + .triggerNative('click') +} + +asyncTest('link with "data-method" set to "delete"', 3, function() { + submit(function(data) { + equal(data.REQUEST_METHOD, 'DELETE') + strictEqual(data.params.authenticity_token, undefined) + strictEqual(data.HTTP_X_CSRF_TOKEN, undefined) + }) +}) + +asyncTest('click on the child of link with "data-method"', 3, function() { + $(document).bind('iframe:loaded', function(e, data) { + equal(data.REQUEST_METHOD, 'DELETE') + strictEqual(data.params.authenticity_token, undefined) + strictEqual(data.HTTP_X_CSRF_TOKEN, undefined) + start() + }) + $('#qunit-fixture a').html('<strong>destroy!</strong>').find('strong').triggerNative('click') +}) + +asyncTest('link with "data-method" and CSRF', 1, function() { + $('#qunit-fixture') + .append('<meta name="csrf-param" content="authenticity_token"/>') + .append('<meta name="csrf-token" content="cf50faa3fe97702ca1ae"/>') + + submit(function(data) { + equal(data.params.authenticity_token, 'cf50faa3fe97702ca1ae') + }) +}) + +asyncTest('link "target" should be carried over to generated form', 1, function() { + $('a[data-method]').attr('target', 'super-special-frame') + submit(function(data) { + equal(data.params._target, 'super-special-frame') + }) +}) + +asyncTest('link with "data-method" and cross origin', 1, function() { + var data = {} + + $('#qunit-fixture') + .append('<meta name="csrf-param" content="authenticity_token"/>') + .append('<meta name="csrf-token" content="cf50faa3fe97702ca1ae"/>') + + $(document).on('submit', 'form', function(e) { + $(e.currentTarget).serializeArray().map(function(item) { + data[item.name] = item.value + }) + + return false + }) + + var link = $('#qunit-fixture').find('a') + + link.attr('href', 'http://www.alfajango.com') + + link.triggerNative('click') + + start() + + notEqual(data.authenticity_token, 'cf50faa3fe97702ca1ae') +}) + +})() diff --git a/actionview/test/ujs/public/test/data-remote.js b/actionview/test/ujs/public/test/data-remote.js new file mode 100644 index 0000000000..a51aa10417 --- /dev/null +++ b/actionview/test/ujs/public/test/data-remote.js @@ -0,0 +1,400 @@ +module('data-remote', { + setup: function() { + $('#qunit-fixture') + .append($('<a />', { + href: '/echo', + 'data-remote': 'true', + 'data-params': 'data1=value1&data2=value2', + text: 'my address' + })) + .append($('<button />', { + 'data-url': '/echo', + 'data-remote': 'true', + 'data-params': 'data1=value1&data2=value2', + text: 'my button' + })) + .append($('<form />', { + action: '/echo', + 'data-remote': 'true', + method: 'post', + id: 'my-remote-form' + })) + .append($('<a />', { + href: '/echo', + 'data-remote': 'true', + disabled: 'disabled', + text: 'Disabed link' + })) + .find('form').append($('<input type="text" name="user_name" value="john">')) + + } +}) + +asyncTest('ctrl-clicking on a link does not fire ajaxyness', 0, function() { + var link = $('a[data-remote]') + + // Ideally, we'd setup an iframe to intercept normal link clicks + // and add a test to make sure the iframe:loaded event is triggered. + // However, jquery doesn't actually cause a native `click` event and + // follow links using `trigger('click')`, it only fires bindings. + link + .removeAttr('data-params') + .bindNative('ajax:beforeSend', function() { + ok(false, 'ajax should not be triggered') + }) + + link.triggerNative('click', { metaKey: true }) + link.triggerNative('click', { ctrlKey: true }) + + setTimeout(function() { start() }, 13) +}) + +asyncTest('ctrl-clicking on a link still fires ajax for non-GET links and for links with "data-params"', 2, function() { + var link = $('a[data-remote]') + + link + .removeAttr('data-params') + .attr('data-method', 'POST') + .bindNative('ajax:beforeSend', function() { + ok(true, 'ajax should be triggered') + }) + .triggerNative('click', { metaKey: true }) + + link + .removeAttr('data-method') + .attr('data-params', 'name=steve') + .triggerNative('click', { metaKey: true }) + + setTimeout(function() { start() }, 13) +}) + +asyncTest('clicking on a link with data-remote attribute', 5, function() { + $('a[data-remote]') + .bindNative('ajax:success', function(e, data, status, xhr) { + App.assertCallbackInvoked('ajax:success') + App.assertRequestPath(data, '/echo') + console.log(data.params) + equal(data.params.data1, 'value1', 'ajax arguments should have key data1 with right value') + equal(data.params.data2, 'value2', 'ajax arguments should have key data2 with right value') + App.assertGetRequest(data) + }) + .bindNative('ajax:complete', function() { start() }) + .triggerNative('click') +}) + +asyncTest('clicking on a link with both query string in href and data-params', 4, function() { + $('a[data-remote]') + .attr('href', '/echo?data3=value3') + .bindNative('ajax:success', function(e, data, status, xhr) { + App.assertGetRequest(data) + equal(data.params.data1, 'value1', 'ajax arguments should have key data1 with right value') + equal(data.params.data2, 'value2', 'ajax arguments should have key data2 with right value') + equal(data.params.data3, 'value3', 'query string in url should be passed to server with right value') + }) + .bindNative('ajax:complete', function() { start() }) + .triggerNative('click') +}) + +asyncTest('clicking on a link with both query string in href and data-params with POST method', 4, function() { + $('a[data-remote]') + .attr('href', '/echo?data3=value3') + .attr('data-method', 'post') + .bindNative('ajax:success', function(e, data, status, xhr) { + App.assertPostRequest(data) + equal(data.params.data1, 'value1', 'ajax arguments should have key data1 with right value') + equal(data.params.data2, 'value2', 'ajax arguments should have key data2 with right value') + equal(data.params.data3, 'value3', 'query string in url should be passed to server with right value') + }) + .bindNative('ajax:complete', function() { start() }) + .triggerNative('click') +}) + +asyncTest('clicking on a link with disabled attribute', 0, function() { + $('a[disabled]') + .bindNative('ajax:before', function(e, data, status, xhr) { + App.assertCallbackNotInvoked('ajax:success') + }) + .bindNative('ajax:complete', function() { start() }) + .triggerNative('click') + + setTimeout(function() { + start() + }, 13) +}) + +asyncTest('clicking on a button with data-remote attribute', 5, function() { + $('button[data-remote]') + .bindNative('ajax:success', function(e, data, status, xhr) { + App.assertCallbackInvoked('ajax:success') + App.assertRequestPath(data, '/echo') + equal(data.params.data1, 'value1', 'ajax arguments should have key data1 with right value') + equal(data.params.data2, 'value2', 'ajax arguments should have key data2 with right value') + App.assertGetRequest(data) + }) + .bindNative('ajax:complete', function() { start() }) + .triggerNative('click') +}) + +asyncTest('changing a select option with data-remote attribute', 5, function() { + $('form') + .append( + $('<select />', { + 'name': 'user_data', + 'data-remote': 'true', + 'data-params': 'data1=value1', + 'data-url': '/echo' + }) + .append($('<option />', {value: 'optionValue1', text: 'option1'})) + .append($('<option />', {value: 'optionValue2', text: 'option2'})) + ) + + $('select[data-remote]') + .bindNative('ajax:success', function(e, data, status, xhr) { + App.assertCallbackInvoked('ajax:success') + App.assertRequestPath(data, '/echo') + equal(data.params.user_data, 'optionValue2', 'ajax arguments should have key term with right value') + equal(data.params.data1, 'value1', 'ajax arguments should have key data1 with right value') + App.assertGetRequest(data) + }) + .bindNative('ajax:complete', function() { start() }) + .val('optionValue2') + .triggerNative('change') +}) + +asyncTest('submitting form with data-remote attribute', 4, function() { + $('form[data-remote]') + .bindNative('ajax:success', function(e, data, status, xhr) { + App.assertCallbackInvoked('ajax:success') + App.assertRequestPath(data, '/echo') + equal(data.params.user_name, 'john', 'ajax arguments should have key user_name with right value') + App.assertPostRequest(data) + }) + .bindNative('ajax:complete', function() { start() }) + .triggerNative('submit') +}) + +asyncTest('submitting form with data-remote attribute should include inputs in a fieldset only once', 3, function() { + $('form[data-remote]') + .append('<fieldset><input name="items[]" value="Item" /></fieldset>') + .bindNative('ajax:success', function(e, data, status, xhr) { + App.assertCallbackInvoked('ajax:success') + equal(data.params.items.length, 1, 'ajax arguments should only have the item once') + App.assertPostRequest(data) + }) + .bindNative('ajax:complete', function() { + $('form[data-remote], fieldset').remove() + start() + }) + .triggerNative('submit') +}) + +asyncTest('submitting form with data-remote attribute submits input with matching [form] attribute', 5, function() { + $('#qunit-fixture') + .append($('<input type="text" name="user_data" value="value1" form="my-remote-form">')) + + $('form[data-remote]') + .bindNative('ajax:success', function(e, data, status, xhr) { + App.assertCallbackInvoked('ajax:success') + App.assertRequestPath(data, '/echo') + equal(data.params.user_name, 'john', 'ajax arguments should have key user_name with right value') + equal(data.params.user_data, 'value1', 'ajax arguments should have key user_data with right value') + App.assertPostRequest(data) + }) + .bindNative('ajax:complete', function() { start() }) + .triggerNative('submit') +}) + +asyncTest('submitting form with data-remote attribute by clicking button with matching [form] attribute', 5, function() { + $('form[data-remote]') + .bindNative('ajax:success', function(e, data, status, xhr) { + App.assertCallbackInvoked('ajax:success') + App.assertRequestPath(data, '/echo') + equal(data.params.user_name, 'john', 'ajax arguments should have key user_name with right value') + equal(data.params.user_data, 'value2', 'ajax arguments should have key user_data with right value') + App.assertPostRequest(data) + }) + .bindNative('ajax:complete', function() { start() }) + + $('<button />', { + type: 'submit', + name: 'user_data', + value: 'value1', + form: 'my-remote-form' + }) + .appendTo($('#qunit-fixture')) + + $('<button />', { + type: 'submit', + name: 'user_data', + value: 'value2', + form: 'my-remote-form' + }) + .appendTo($('#qunit-fixture')) + .triggerNative('click') +}) + +asyncTest('form\'s submit bindings in browsers that don\'t support submit bubbling', 5, function() { + var form = $('form[data-remote]'), directBindingCalled = false + + ok(!directBindingCalled, 'nothing is called') + + form + .append($('<input type="submit" />')) + .bindNative('submit', function(event) { + ok(event.type == 'submit', 'submit event handlers are called with submit event') + ok(true, 'binding handler is called') + directBindingCalled = true + }) + .bindNative('ajax:beforeSend', function() { + ok(true, 'form being submitted via ajax') + ok(directBindingCalled, 'binding handler already called') + }) + .bindNative('ajax:complete', function() { + start() + }) + + if(!$.support.submitBubbles) { + // Must indrectly submit form via click to trigger jQuery's manual submit bubbling in IE + form.find('input[type=submit]') + .triggerNative('click') + } else { + form.triggerNative('submit') + } +}) + +asyncTest('returning false in form\'s submit bindings in non-submit-bubbling browsers', 1, function() { + var form = $('form[data-remote]') + + form + .append($('<input type="submit" />')) + .bindNative('submit', function() { + ok(true, 'binding handler is called') + return false + }) + .bindNative('ajax:beforeSend', function() { + ok(false, 'form should not be submitted') + }) + + if (!$.support.submitBubbles) { + // Must indrectly submit form via click to trigger jQuery's manual submit bubbling in IE + form.find('input[type=submit]').triggerNative('click') + } else { + form.triggerNative('submit') + } + + setTimeout(function() { start() }, 13) +}) + +asyncTest('clicking on a link with falsy "data-remote" attribute does not fire ajaxyness', 0, function() { + $('a[data-remote]') + .attr('data-remote', 'false') + .bindNative('ajax:beforeSend', function() { + ok(false, 'ajax should not be triggered') + }) + .bindNative('click', function() { + return false + }) + .triggerNative('click') + + setTimeout(function() { start() }, 20) +}) + +asyncTest('ctrl-clicking on a link with falsy "data-remote" attribute does not fire ajaxyness even if "data-params" present', 0, function() { + var link = $('a[data-remote]') + + link + .removeAttr('data-params') + .attr('data-remote', 'false') + .attr('data-method', 'POST') + .bindNative('ajax:beforeSend', function() { + ok(false, 'ajax should not be triggered') + }) + .bindNative('click', function() { + return false + }) + .triggerNative('click', { metaKey: true }) + + link + .removeAttr('data-method') + .attr('data-params', 'name=steve') + .triggerNative('click', { metaKey: true }) + + setTimeout(function() { start() }, 20) +}) + +asyncTest('clicking on a button with falsy "data-remote" attribute', 0, function() { + $('button[data-remote]:first') + .attr('data-remote', 'false') + .bindNative('ajax:beforeSend', function() { + ok(false, 'ajax should not be triggered') + }) + .bindNative('click', function() { + return false + }) + .triggerNative('click') + + setTimeout(function() { start() }, 20) +}) + +asyncTest('submitting a form with falsy "data-remote" attribute', 0, function() { + $('form[data-remote]:first') + .attr('data-remote', 'false') + .bindNative('ajax:beforeSend', function() { + ok(false, 'ajax should not be triggered') + }) + .bindNative('submit', function() { + return false + }) + .triggerNative('submit') + + setTimeout(function() { start() }, 20) +}) + +asyncTest('changing a select option with falsy "data-remote" attribute', 0, function() { + $('form') + .append( + $('<select />', { + 'name': 'user_data', + 'data-remote': 'false', + 'data-params': 'data1=value1', + 'data-url': '/echo' + }) + .append($('<option />', {value: 'optionValue1', text: 'option1'})) + .append($('<option />', {value: 'optionValue2', text: 'option2'})) + ) + + $('select[data-remote=false]:first') + .bindNative('ajax:beforeSend', function() { + ok(false, 'ajax should not be triggered') + }) + .val('optionValue2') + .triggerNative('change') + + setTimeout(function() { start() }, 20) +}) + +asyncTest('form should be serialized correctly', 6, function() { + $('form') + .append('<textarea name="textarea">textarea</textarea>') + .append('<input type="checkbox" name="checkbox[]" value="0" />') + .append('<input type="checkbox" checked="checked" name="checkbox[]" value="1" />') + .append('<input type="radio" checked="checked" name="radio" value="0" />') + .append('<input type="radio" name="radio" value="1" />') + .append('<select multiple="multiple" name="select[]">\ + <option value="1" selected>1</option>\ + <option value="2" selected>2</option>\ + <option value="3">3</option>\ + <option selected>4</option>\ + </select>') + .bindNative('ajax:success', function(e, data, status, xhr) { + equal(data.params.checkbox.length, 1) + equal(data.params.checkbox[0], '1') + equal(data.params.radio, '0') + equal(data.params.select.length, 3) + equal(data.params.select[2], '4') + equal(data.params.textarea, 'textarea') + + start() + }) + .triggerNative('submit') +}) diff --git a/actionview/test/ujs/public/test/override.js b/actionview/test/ujs/public/test/override.js new file mode 100644 index 0000000000..be6ec7749b --- /dev/null +++ b/actionview/test/ujs/public/test/override.js @@ -0,0 +1,56 @@ +(function() { + +var realHref + +module('override', { + setup: function() { + realHref = $.rails.href + $('#qunit-fixture') + .append($('<a />', { + href: '/real/href', 'data-remote': 'true', 'data-method': 'delete', 'data-href': '/data/href' + })) + }, + teardown: function() { + $.rails.href = realHref + } +}) + +asyncTest('the getter for an element\'s href is publicly accessible', 1, function() { + ok($.rails.href) + start() +}) + +asyncTest('the getter for an element\'s href is overridable', 1, function() { + $.rails.href = function(element) { return $(element).data('href') } + $('#qunit-fixture a') + .bindNative('ajax:beforeSend', function(e, xhr, options) { + equal('/data/href', options.url) + return false + }) + .triggerNative('click') + start() +}) + +asyncTest('the getter for an element\'s href works normally if not overridden', 1, function() { + $('#qunit-fixture a') + .bindNative('ajax:beforeSend', function(e, xhr, options) { + equal(location.protocol + '//' + location.host + '/real/href', options.url) + return false + }) + .triggerNative('click') + start() +}) + +asyncTest('the event selector strings are overridable', 1, function() { + ok($.rails.linkClickSelector.indexOf(', a[data-custom-remote-link]') != -1, 'linkClickSelector contains custom selector') + start() +}) + +asyncTest('including jquery-ujs multiple times throws error', 1, function() { + throws(function() { + Rails.start() + }, 'appending rails.js again throws error') + setTimeout(function() { start() }, 50) +}) + +})() diff --git a/actionview/test/ujs/public/test/settings.js b/actionview/test/ujs/public/test/settings.js new file mode 100644 index 0000000000..3375456f11 --- /dev/null +++ b/actionview/test/ujs/public/test/settings.js @@ -0,0 +1,116 @@ +var App = App || {} + +App.assertCallbackInvoked = function(callbackName) { + ok(true, callbackName + ' callback should have been invoked') +} + +App.assertCallbackNotInvoked = function(callbackName) { + ok(false, callbackName + ' callback should not have been invoked') +} + +App.assertGetRequest = function(requestEnv) { + equal(requestEnv['REQUEST_METHOD'], 'GET', 'request type should be GET') +} + +App.assertPostRequest = function(requestEnv) { + equal(requestEnv['REQUEST_METHOD'], 'POST', 'request type should be POST') +} + +App.assertRequestPath = function(requestEnv, path) { + equal(requestEnv['PATH_INFO'], path, 'request should be sent to right url') +} + +App.getVal = function(el) { + return el.is('input,textarea,select') ? el.val() : el.text() +} + +App.disabled = function(el) { + return el.is('input,textarea,select,button') ? + (el.is(':disabled') && $.rails.getData(el[0], 'ujs:disabled')) : + $.rails.getData(el[0], 'ujs:disabled') +} + +App.checkEnabledState = function(el, text) { + ok(!App.disabled(el), el.get(0).tagName + ' should not be disabled') + equal(App.getVal(el), text, el.get(0).tagName + ' text should be original value') +} + +App.checkDisabledState = function(el, text) { + ok(App.disabled(el), el.get(0).tagName + ' should be disabled') + equal(App.getVal(el), text, el.get(0).tagName + ' text should be disabled value') +} + +// hijacks normal form submit; lets it submit to an iframe to prevent +// navigating away from the test suite +$(document).bind('submit', function(e) { + if (!e.isDefaultPrevented()) { + var form = $(e.target), action = form.attr('action'), + name = 'form-frame' + jQuery.guid++, + iframe = $('<iframe name="' + name + '" />'), + iframeInput = '<input name="iframe" value="true" type="hidden" />' + targetInput = '<input name="_target" value="' + (form.attr('target') || '') + '" type="hidden" />' + + if (action && action.indexOf('iframe') < 0) { + if (action.indexOf('?') < 0) { + form.attr('action', action + '?iframe=true') + } else { + form.attr('action', action + '&iframe=true') + } + } + form.attr('target', name).append(iframeInput, targetInput) + $('#qunit-fixture').append(iframe) + $.event.trigger('iframe:loading', form) + } +}) + +var MouseEvent = window.MouseEvent + +try { + new MouseEvent() +} catch (e) { + MouseEvent = function(type, options) { + var evt = document.createEvent('MouseEvents') + evt.initMouseEvent(type, options.bubbles, options.cancelable, window, options.detail, 0, 0, 80, 20, options.ctrlKey, options.altKey, options.shiftKey, options.metaKey, 0, null) + return evt + } +} + +$.fn.extend({ + // trigger an native click event + triggerNative: function(type, options) { + var el = this[0], + event, + Evt = { + 'click': MouseEvent, + 'change': Event, + 'pageshow': PageTransitionEvent, + 'submit': Event + }[type] + + options = options || {} + options.bubbles = true + options.cancelable = true + + event = new Evt(type, options) + + el.dispatchEvent(event) + + if (type === 'submit' && !event.defaultPrevented) { + el.submit() + } + return this + }, + bindNative: function(event, handler) { + if (!handler) return this + + this.bind(event, function(e) { + var args = [] + if (e.originalEvent.detail) { + args = e.originalEvent.detail.slice() + } + args.unshift(e) + return handler.apply(this, args) + }) + return this + } +}) |