How to find out why rails feature test fails - ruby-on-rails

we're currently working on a piece of mapping software, where we use Leaflet with custom left and right sidebars as well as a text-filter where we filter for different POI features. The whole thing looks like this:
The flow is as follows
A user visits a map under a unique link
The controller renders the HTML template first (no data is bein published)
Inside our javascript an ajax call fetches the data and renders markers, some panels, etc., etc.
We use capybara with poltergeist for all our feature tests.
In our master everything is working as it should be.
In another branch I added password protection, hence a bootstrap modal pops up if a map is password protected and has not yet been unlocked within the current session.
Everything is working fine except for some feature tests that fail lately and after messing around with stuff I still don't have a clue why exactly.
Let's see for example this test
feature 'Places map filter', js: true do
before do
#map = create :map, :full_public
create :place, :unreviewed, categories: 'Playground', map: #map
visit map_path(map_token: #map.public_token)
find('.open-sidebar').trigger('click')
end
scenario 'Nothing filters nothing' do
show_places
show_events
show_places_list_panel
expect(page).to <...>
end
...
end
Capybara claims to be unable to find some css elements. Calling screenshot_and_open_image reveals that it is still showing an overlay (hiding everything else) until all data have been loaded. Something seems to be hanging within my Javascript...
.
I've been messing around with the test-environment, which had an effect:
config.action_controller.asset_host = "file://#{::Rails.root}/public"
config.assets.prefix = 'assets_test'
The test passes since the data is now there. A screenshot reveals missing assets, which is guided by a proper warning message Not allowed to load local resource: <path>. I'm puzzled since querying the data happens via an ajax-call from one of the files that capybara tells to be unaccessible.
I don't know how to continue, since I don't want to start skipping tests. I hope you can help guiding me finding the error.
Thanks in advance,
Andi
Update
Thanks to Thomas for his hint on ES6 features. I used poltergeist's inspector mode and hence was able to discover an arrow function I introduced! That's why the JS driver couldn't deal with a callback I was passing to a promise which did not resolve...

Firstly, ensure you have js_errors: true in your Poltergeist driver registration - https://github.com/teampoltergeist/poltergeist#customization - so that you will get runtime JS errors reported.
Secondly, if you're using any ES6+ features in your JS code, make sure you transpiling them into ES5 compatible code since Poltergeist/PhantomJS only supports JS <= ES5, and will silently fail at JS parse time if it parses JS using features like let.
And finally, by using trigger you are bypassing Poltergeists checks that the button is actually clickable by the user, so make sure you're not clicking a button too early (before whatever behavior gets attached to the button is actually attached)

Related

StaleElementReference Error Element not found in the cache

I'm using Capybara 2.1 with Ruby 1.9.3 using the selenium driver (with Minitest and Test Unit) in order to test a web app.
I am struggling with the StaleElementReferenceException problem. I have seen quite a number of discussions on the topic but I haven't been able to find a solution to the issue that I am facing.
So basically, I'm trying to find all pagination elements on my page using this code:
pagination_elements = page.all('.pagination a')
Then I'm doing some assertions on those elements like:
pagination_elements.first.must_have_content('1')
After those assertions, I'm continuing the test by clicking on the Next Page link to make sure that my future first pagination element will be the Previous Page.
To do that I'm retrieving paginations elements again :
new_pagination_elements = page.all('.pagination a')
And the Stale Error is occurring here, because I'm reaching elements that I've already reached. ( Here is the error )
You can see the link states here.
I really have no idea how to make this common test work properly.
Do you have any tips for a better way to reach my pagination elements?
I sometimes have some problem with AJAX intensive pages, in my case this workaround solves it:
begin
...
rescue Selenium::WebDriver::Error::StaleElementReferenceError
sleep 1
retry
end
I saw the main message in the gist is:
Element not found in the cache -
perhaps the page has changed since it was looked up
I have similar case before. There are two solutions:
Add page.reload before checking same stuff in new page, if you have set Capybara.automatic_reload = false in spec_helper
find a special element in new page which previous page doesn't have. This effect is equivalent to wait.
Another method is to use specific selector. For example, instead of
pagination_elements = page.all('.pagination a')
Use
pagination_elements = page.all('#post_123 .pagination a')
Append a unique id area to the selector and you should not meet such problem.
Interesting link about this error and how to fix it : http://stefan.haflidason.com/testing-with-rails-and-capybara-methods-that-wait-method-that-wont/
Apparently, in addition to race conditions, this error also appears due to misused within blocks. For example:
within '.edit_form' do
click '.edit_button'
# The error will appear here if the 'edit_button' is not a
# descendant of the 'edit_form'
end
HAve you tried to use WebDriver directly rather than via Capybara? This woudl potentially give you more control of when to and when to not cache objects.
e.g. (Apologies for the java syntax but should get the idea)
WebElement searchField = driver.findElement(By.CssSelector("input.foo"));
searchField.click();
searchField.sendKeys("foo foo");
System.out.println(searchField.getText());
//Do something elsewhere on the page which causes html to change (e.g. submit form)
.....
....
//This next line would throw stale object
System.out.println(searchField.getText());
//This line will not throw exception
searchField = driver.findElement(By.CssSelector("input.foo"));
System.out.println(searchField.getText());
Assigning "findElement" again to "searchField" means that we re-find the element. Knowing when to and when not re-assign is key went deciding how to cache your webelements.
I have not used Capybara, but I assume that it hides the caching strategy from you?

How to fill tinymce-rails editor with capybara and selenium?

I have trouble with using capybara to test tinymce form. I'm using tinymce-rails and have 7 editors in my form. Also I'm using asciimath plugin with tinymce.
Everything works fine, but I'm unable to write tests to fill in tinymce editor.
Here is how my step definition code looks like, very similar to what is described here:
within_frame("content_ifr") do
editor = page.find_by_id('tinymce')
editor.native.send_keys 'test'
end
The problem is when I run the following:
editor.native.clear # works, clear the editor area, I'm testing this with pry
editor.native.send_keys :tab # works, moves focus to next input
editor.native.send_keys 'test' # returns "", nothing happens, nothing in editor
So clear and send_keys :tab work as expected. But I can't send any string. send_keys function is always returning empty string, and nothing happens when I do test using pry.
What is going wrong here? and how can I debug / investigate the problem?
Thanks.
I know that this is an old question but I just found it while trying to solve this issue as well.
Although the original question said that he has 7 tinymce's on the same page I think that my solution might work for him too but I do know it will work if there is one tinymce as was my case.
In my request spec I used this:
page.execute_script('$(tinymce.editors[0].setContent("my content here"))')
The page.execute_script with tell it to run the jQuery function. It then finds the first tincymce editor and sets the content.
Worked like a charm for me. I think if there are more than one tinymce it can be called by its position.
Switching to chrome as described here solved my problem.
Obviously the problem is related with a bug in firefox driver.
Still i think it is a valid question for firefox.
Try to switch to an iframe that contains tinymce textarea input, and than send_keys:
# +session+ is an instance of Capybara::Session class
browser = session.driver.browser
browser.switch_to.frame(iframe_id)
editor.native.send_keys(text)
browser.switch_to.default_content
I had the same issue. After a day fighting, my tests finally passed.
The code that I am using is:
within_frame("producto_condiciones_ifr") do
editor = page.find_by_id('tinymce')
editor.native.send_keys 'filling text'
end
The first line is a method of capybara. The parameter passed is the ID of the iframe.
Line #2 is a must.
In line #3 goes the text that you wish to place inside TinyMCE
just came across this problem with RoR and rspec
I managed to solve by doing this:
within_frame { page.find_by_id("tinymce").set("new content here") }
the set method will replace any existing content by the new one
if you want to keep the current content and add things to it, use the send_keys method

Rails3 Google Maps testing with selenium

I'm trying to test a google maps app with Rails3. I'm using cucumber with capybara and selenium for JavaScript testing.
I have a map where I wait for google maps to be loaded, and then send an ajax request to my server, where I get locations that I'm inserting into the map.
I'm wondering if it's possible with selenium to wait until google maps is loaded, the ajax call to my server is finished and the marker are placed inside the map. The other issue is, how to select this marker within google maps. Are there any selectors?
Or should I go the other way, and use a JS testing framework like Jasmine to test if my classes are loaded and so on. I don't have any experience with Jasmine, so is it possible to test a google maps?
Maybe someone knows a solution, or a hint if it's not possible, or a workaround or... ;)
[UPDATE 1]
I've found out how to select markers in google maps. If you look at googles selenium tests you can check out what they are doing. For example selecting a marker:
waitForElementPresent xpath=//img[contains(#src,'marker')]
But here comes the next problem. How do I select a specific marker? Is there a way inside the javascript google maps API to assign an ID to it, so that I can use #marker_1, #marker_2...?
And another strange thing is, that function like wait_for_element or wait_for_condition aren't available inside my cucumber step definitions. Are the google selenium tests using own function like that waitForElementPresent? Or are this standard selenium functions? I've found a lots of posts, where they always use something like
selenium.wait_for_condition
selenium.wait_for_element
or
#selenium.wait_for_condition
...
Inside my step definitions the selenium and the #selenium var a nil. How can I access this methods? I've also found this post, but it is from Oct. '08, so I think there must be a better solution (btw. this solution works on the first sight).
Like on this page, they give an overview of a few selenium methods how to wait for a condition or element. Is this still present? How can I use this functions?
[UPDATE 2]
Damn it, I've found out, that the selenium tests I mentioned above are for V2 of google maps, not for V3. I have tried it with
wait_until { page.has_xpath?("//img[contains(#src,'marker')]") }
But it doesn't work. The marker is visible on the map, but I get a timeout error, because with this XPath selector it is not found. I'm wondering if it is generally possible to select a marker out of the DOM.
I also tried to assign an additional attribute to the marker when I create it:
// marker is the marker returned by google maps after adding it to the map
$(marker).attr('id', "marker_1");
But when I try to access it with the jQuery selector $("#marker_1"), it doesn't find it. So, still no solution, yet.
What I do with mine is to execute the calls in your step definitions like so:
page.execute_script("launchmap()")
then check for their existence in the page..then do your normal ajax check in capybara. the marker will be contained in a div right? then if you call launchmap and create the markers, capybara SHOULD be able find your markers
UPDATE
found out about this plugin: http://vigetlabs.github.com/jmapping/examples/
it gives you semantic markup for your google maps(for graceful degradation) allowing you to actually check if a marker exists using capybara. hope it helps(dont have time to test it but it looks promising)
I found a way to Integration test my Google map with RSpec and Capybara. I wanted to write a test to assert that the content I got from an internal JSON-feed ended up in form of markers and info windows on my map (Google Maps API V3) in a Rails 4 app.
The problem:
Even when I ran the tests with Selenium Webdriver that supports JS, I didn't find a way to select the markers with Capybara to check for their content.
My solution:
1. I added a div with ID test-box to my HTML markup
2. I wrote three helper methods into my JS that had access to my maps markers and added the results to the test-box.
3. In Capybara I executed the helper methods and expected to find the expected content or values in the box.
The code:
HTML:
<div id="map-canvas"></div>
<div id="info-box"></div>
JS-helpers:
in app/assets/javascripts/events.js
// Test helper methods
testInfo = function(){
document.getElementById("info-box").innerHTML = markers[0].title;
};
testInfoCount = function(){
document.getElementById("info-box").innerHTML = markers.length;
};
testInfoWindow = function(){
document.getElementById("info-box").innerHTML = markers[0].title + ", " + markers[0].time;
};
"markers" in my code is an array I push in every marker after I have added it to the map. I can be sure that the content actually is on the map if it's in the markers array.
Test:
spec/features/map_feature_spec.rb:
require "rails_helper"
describe "Map", js: true do
let!(:john){create(:user)}
let!(:event1){create(:event, user: john)}
let!(:event2){create(:event)}
it "shows a marker for a geocoded event on front page" do
visit '/'
find('#main-map-canvas')
page.execute_script('testInfo()')
expect(page.find("div#info-box").text).to eq(event1.title)
end
it "shows a marker for each geocoded event on front page" do
visit '/'
find('#main-map-canvas')
page.execute_script('testInfoCount()')
expect(page.find("div#info-box").text).to eq("2")
end
it "shows a marker for an event on event's page" do
visit "/events/#{event1.id}"
expect(page).to have_css("#single-map-canvas")
page.execute_script('testInfo()')
expect(page.find("div#info-box").text).to eq(event1.title)
end
context "Tooltips" do
let!(:event1){create(:event)}
let!(:event2){create(:event)}
it "shows title and date on frontpage" do
visit "/"
find('#main-map-canvas')
page.execute_script('testInfoWindow()')
expect(page.find("div#info-box")).to have_content("Pauls Birthday Party, #{event1.nice_date}")
end
end
end
To run the javascripts Selenium webdriver and Firefox need to be installed (''gem "selenium-webdriver"'' in your Gemfile)
I'm creating the test content (let!...) using Factory Girl.
Running the tests you will actually see the markers on the map and their content appears in the test-box.
To your first question - use waitForCondition with a script that tests for the presense of the markers.
waitForCondition ( script,timeout ) Runs the specified JavaScript snippet repeatedly until it evaluates to "true". The snippet may have multiple lines, but only the result of the last line will be considered. Note that, by default, the snippet will be run in the runner's test window, not in the window of your application. To get the window of your application, you can use the JavaScript snippet selenium.browserbot.getCurrentWindow(), and then run your JavaScript in there
Arguments:
script - the JavaScript snippet to run timeout - a timeout in milliseconds, after which this command will return with an error
Be sure to enable the url capybara is using to run the test server into google console for your app.
You can check this URL (and the error message while loading google maps) by setting config.debug= true within Capybara driver config loading block.

Making tagsoup markup cleansing optional

Tagsoup is interfering with input and formatting it incorrectly. For instance when we have the following markup
Text outside anchor
It is formatted as follows
Text outside anchor
This is a simple example but we have other issues as well. So we made tagsoup cleanup/formatting optional by adding an extra attribute to textarea control.
Here is the diff(https://github.com/binnyg/orbeon-forms/commit/044c29e32ce36e5b391abfc782ee44f0354bddd3).
Textarea would now look like this
<textarea skip-cleanmarkup="true" mediatype="text/html" />
Two questions
Is this the right approach?
If I provide a patch can it make it to orbeon codebase?
Thanks
BinnyG
Erik, Alex, et al
I think there are two questions here:
The first Concern is a question of Tag Soup and the clean up that happens OOTB: Empty tags are converted to singleton tags which when consumed/sent to the client browser as markup gets "fixed" by browsers like firefox but because of the loss of precision they do the wrong thing.
Turning off this clean up helps in this case but for this issue alone is not really the right answer because we it takes away a security feature and a well-formed markup feature... so there may need to be some adjustment to the handling of at least certain empty tags (other than turning them in to invalid singleton tags.)
All this brings us to the second concern which is do we always want those features in play? Our use-case says no. We want the user to be able to spit out whatever markup they want, invalid or not. We're not putting the form in an app that needs to protect the user from cross script coding, we're building a tool that lets users edit web pages -- hence we have turned off the clean-up.
But turning off cleanup wholesale? Well it's important that we can do it if that's what our usecase calls for but the implementation we have is all or nothing. It would be nice to be able to define strategies for cleanup. Make that function plug-able. For example:
* In the XML Config of the system define a "map" of config names to class names which implement the a given strategy. In the XForm Def the author would specify the name from the map.
If TagSoup transforms:
Text outside anchor
Into:
Text outside anchor
Wouldn't that be bug in TagSoup? If that was the case, then I'd say that it is better to fix this issue rather than disable TagSoup. But, it isn't a bug in TagSoup; here is what seems to be happening. Say the browsers sends the following to the client:
<a shape="rect"></a>After<br clear="none">
This goes through TagSoup, the result goes through the XSLT clean-up code, and the following is sent to the browser:
<a shape="rect"/>After<br clear="none"/>
The issue is on the browser, which transforms this into:
<a shape="rect">After</a><br clear="none"/>
The problem is that we serialize this as XML with Dom4jUtils.domToString(cleanedDocument), while it would be more prudent to serialize it as HTML. Here we could use the Saxon serializer. It is also used from HTMLSerializer. Maybe you can try changing this code to use it instead of using Dom4jUtils.domToString(). You'll let us know what you find when a get a chance to do that.
Binesh and I agree, if there is a bug it would be a good idea to address the issue closer to the root. But I think the specific issue he is only part of the matter.
We're thinking it would be best to have some kind of name-to-strategy mapping so that RTEs can call in the server-side processing that is right for them or the default if it's not specified.

Django admin/doc/views/ all blank or broken

I'm trying to use Django's built-in admin docs feature to make writing templates easier. Supposedly if you go to /admin/docs/views you should get documentation for every view in your application. I see a list, but none of the links work:
-) Any view listed that's related to my application just goes to a blank page with nothing but the name of the view as a header.
-) The views related to admin all give me Django 404 errors when I click on them, except those that are related to the docs itself. The docs-related links also give me blank pages. (i.e. clicking /admin/doc/filters gives a blank page with nothing but "django.contrib.admindocs.views.template_filter_index" as a title, but clicking /admin/auth/user gives me a Django 404 error
The 404 errors lead me to suspect my URLconf is wrong, but all I did was uncomment the built-in lines. The relevant sections read:
# Uncomment the admin/doc line below to enable admin documentation:
(r'^admin/doc/', include('django.contrib.admindocs.urls')),
# Uncomment the next line to enable the admin:
(r'^admin/', include(admin.site.urls)),
And I have no idea what to make of the blank pages. Do I need to provide some extra meta information somewhere, like I know you need to provide the get_absolute_url on models for some of the admin features to work right?
Even if no one knows the answer, any documentation on the admin docs feature would be useful -- I've been Google all over (and searching StackOverflow) and this feature seems very little-documented.
Thanks!
You need to add 'django.contrib.admindocs' to your INSTALLED_APPS in settings.py. It should already be there and commented out. Though it would be nice if the comment in urls.py mentioned it ... Source.
I've never looked at the views admin doc pages before -- I've never had a need to. B4ut you're right, they seem to be -- lacking in potential features.
If you give your views functions docstrings (documentation), that content will appear on your "blank pages".
Most -- no, all -- of the admin sites views are actually decorated member methods of admin.sites.AdminSite. I looked around, and a view of mine which uses a decorator also suffers from the 404.
The view responsible for view details starts:
def view_detail(request, view):
if not utils.docutils_is_available:
return missing_docutils_page(request)
mod, func = urlresolvers.get_mod_func(view)
try:
view_func = getattr(import_module(mod), func)
except (ImportError, AttributeError):
raise Http404
title, body, metadata = utils.parse_docstring(view_func.__doc__)
...
You can see it tries to import the view to get info from it; if the view is actually a decorator (which probably used an internal function to wrap the real view), it won't be able to import it. eg, if you do from django.contrib.admin.sites import index in a django shell, you'll get an ImportError, whereas django.contrib.admin.site.index (note the singular site) is a:
<bound method AdminSite.index of <django.contrib.admin.sites.AdminSite object at 0x...>>
Further, that last line in my snippet seems to indicate that there's a capability for finer control over what shows up on those pages, if you care to figure out the template that util.parse_docstring uses.

Resources