Using javascript global window variables and integration testing - ruby-on-rails

There's this nifty stackoverflow post on passing variables to Javascript. It echos this railscast episode. The technique works like a charm for configuring a jquery datepicker, but cause all my javascript integration tests to fail.
Here is the code in application.html.erb
<script type="text/javascript">
<%-# commented line -%>
window.sDateFormatLocal = "<%= t 'date.formats.js_date' %>"
</script>
This is a datepicker initialization that uses it
$("input.datepicker").datepicker({
dateFormat: sDateFormatLocal,
onClose: function(value, ui) {
console.log("regular old datepicker");
}
}
It appears to work very well. The only problem, all my integration tests with 'js: true' now fail. Here are the errors I get.
Capybara::Poltergeist::JavascriptError:
One or more errors were raised in the Javascript code on the page:
ReferenceError: Can't find variable: sDateFormatLocal
When I run in browser (Chrome, Firefox) there are no errors or warnings in the console.
For completeness, a typical spec looks like this:
describe "The root" do
it "should load the page and have js working.", :js => true do
visit root_path
page.should have_content "Hello world"
end
end
Is there a setting I am missing to allow variables like this in poltergeist?

If your datepicker function is not in jQuery document ready function or similar methods such as window.onload you'll have trouble.
By default the application.js will be loaded in head, and the JS code in your html.erb later. The html and assets are loaded by browser in parallel, and very likely assets will finish loading at first. If you execute the function right away instead of waiting for document ready, the variable is undefined.
If you missed that, put such code in ready block.
A better practice for exporting server variable is, instead of put it in html body, put it on head before application.js so you won't have any problem on undefined variable.

Just to follow-up on this, in case someone in is debugging a similar problem. It turned out in this case, not every view was using the same template. For instance, the signin screen has a different head. That was causing this not to load in certain circumstances, yet not others, like the ones where my tests were failing. Bottom line, make sure when you replicate your tests, you are doing it exactly as the tests do, like in my case, passing through a signin screen.

Related

Capybara prevents attaching onsubmit event to form

In my rails app I attach onsubmit event to my form using jquery like this:
$("form.my_form").submit(function(){
alert("test")
return true;
});
This is working fine when I manually open the page and submit the form (clicking on the submit button) - I see the alert "test"
BUT I have a Capybara test (over Selenium) that loads the page, fills in some values in the form, and then clicks on submit.
And the function is NOT triggered.
Note 1: when running the test the form is indeed submitted, new values recorded in the DB etc. so there is no problem with clicking on the submit button itself
Note 2: if instead of jquery, in the rails view in the form_for tag I put
html: {onsubmit: "onsubmitfunc();"}
with the alert in the onsubmitfunc() - everything works as expected in the capybara test
Note 3: in order for the jquery way to work the js file is defer loaded e.g.:
<%= javascript_include_tag 'application', defer: true %>
I am very new to JQuery, so am I doing something criminal there?
Or Capybara has some problem? with defer loading of the JS file? Or with JQuery? Any ideas?
Since you're using Selenium - the most common reason would be that you have an error in one of your JS assets. When in the dev environment all your JS assets are served in separate files which means an error in one file doesn't stop others from being processed. In the test environment (and production) the assets are concatenated into one file which means an error in any file can cause assets concatenated after it to not be processed.
Check the browsers console log for any JS errors and fix them.

CKEditor script does not work on heavy HTML page

I am trying to use the CKEditor gem with Rails 4.0.3. I have setup everything according to the docs, but it does not work.
I have a main project with a page which contains many html elements. In this page CKEditor does not work, and does not give any errors or warnings, neither on server side nor on client side (Javascript).
But when I created a test project, and applied the same settings, it worked!
After a lot of struggling, I finally came to this conclusion:
CKEditor inserts a bit of Javascript after the textarea which is supposed to converted. Here is the code:
//<![CDATA[
if (typeof CKEDITOR != 'undefined') { CKEDITOR.replace('article_body'); }
//]]>
I think the problem is that, on the page with many html elements, the code above is run before the DOM is completey loaded, so it has no effect. But on the other page the DOM loads quickly and the code is run in time.
One thing that validates my assumption is that if I run the Javascript code above in the Chrome Developer tools' console, when the page has been completely loaded, it does work.
I don't know if I am right or not, but supposing I am right, how can I force the code above to run when the DOM is fully loaded?
I found the problem. It is because I moved the
<%= stylesheet_link_tag "application" %>
line to the end of the file, before the closing body tag. This line:
//<![CDATA[
if (typeof CKEDITOR != 'undefined') { CKEDITOR.replace('article_body'); }
//]]>
was executed before any other Javascript files were loaded, so it did nothing.
And the solution:
One way is to move the stylesheet_link_tag line to top of the file (which I don't like).
Another way is to add a CSS class named ckeditor to the textarea which you want to be converted. For example:
= form_for #article do |f|
= f.text_area :body, class: "ckeditor"
This will tell CKEditor to convert that textarea to a WYSIWYG editor.

Capybara doesn't render page in test

I have a Rails project with Backbone and Capybara for integration tests.
In the test I'm running save_and_open_page and it successfully opens the page in browser with valid Javascript.
But when I'm running puts page.body (right after save_and_open_page) it gives me
//skipped
<div id="main">
Loading...
</div>
</body></html>
And then no have_content and other selectors work.
How is it possible that it saves and opens valid page but has wrong page body?
How to fix it?
The default driver for Capybara is Rack::Test. Your page probably requires javacript which means that Capybara's default driver is not rendering everything correctly. Just set Capybara.default_driver = :selenium. This way the javascript is run correctly.
See also link, which explains how to change the driver for only one testcase and not the whole test suite. Note that if you use selenium as the driver the method save_and_open_page probably won't work.
I needed to add to spec_helper.rb the line
require 'capybara/rspec'
and use default browser driver

Should the javascript_include_tag always load last?

Railscast#369 explains how moving the javascript_include tag to the bottom of application.html.erb can shorten loading time, as it allows the page to load at the same time as the javascript.
From the simple example given, it seems like the javascript tag should ALWAYS be at the bottom of the page. However, the fact that the Rails default has it at the top implies (to me at least), that this may not always be true.
When would someone NOT move the javascript tag to the bottom of application.html.erb?
If you call any javascript prior to it loading then you may run into issues. As an example in a view (blah.html.erb) file you might have:
<script>
jquery('#element_id').append("<p>Error Message</p>");
</script>
Which would look for jQuery being loaded by the include tag. Not a good practice but sometimes occurs.
Also, you may use a js view file (blah.js.erb) which would look for jQuery before it is loaded as well.

jQuery plugins / functions not loading after rails 3.1 upgrade

So I just recently upgraded a large project to rails 3.1. I've got the asset pipeline set up and working. However, a lot of the plugins we use and the custom jQuery functions we've written don't seem to be getting evaluated, although the source is in application.js. For example:
(function($) {
$.fn.searchable = function() {
....
}
})( jQuery );
I see this code in application.js, but anything that tries to use it gets a javascript error, undefined function. However, if I wrap the whole thing in a function definition, and call that function on document ready, then it works.
This is a really ugly workaround. Any ideas why it's not working without wrapping in a function?
Thanks.
maybe you should take a look at 'coffeescript's namescope'. e.g.
//define your js function as:
this.some_function_name() =>
alert('hello~')
//call it as:
this.some_function_name()

Resources