I use Morris.js for graphs in ruby on rails and find it very useful. However, I am not sure how to test the graphs using feature specs in rspec with capybara. At the very least I would like to test the following
the graph is displaying on the page
the right type of graph e.g. a line graph
there is some data being plotted, e.g. check there are two lines in the graph.
How do you do this?
I can now answer the first two parts of the question. If the graph is working, the javascript code inserts an html svg element in the page. So using a feature spec with js: true, you can test for this with:
page.should have_css 'svg'
If you have different graphs on the same page, you can wrap each in a div and test with something like:
within :css, 'div#first_chart' do
page.should have_css 'svg'
end
This does not exactly tell you that you have the right type of Morris graph in there, but should suffice for most purposes.
I still do not know how to check more detailed features like the number of lines appearing on a graph.
Related
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)
I want to understand what the following Capybara syntax means -
find(:xpath, '//*[#id="application-lines"]/div[2]/ul/li[2]/a').click
I particularly don't understand the second attribute of the find method.
It would be great if someone could help me understand the syntax!
That is not something specific of Capybara, that is an XPath, is used to navigate the elements of an XML document.
In this case is looking for a node with id application-lines and inside that element retrieving the second div[2], with an ul element from which retrieves the second li and retrieves the a element inside it. All this ends up doing is a click on the a element found.
You can learn about XPaths here: XPath tutorial
FitNesse .Net (using the FIT test system, not SLIM) supports testing for lists of the form:
element1, element2, ..., elementN
This works well. However, I can't figure out how to test for an empty list.
Empty strings can be tested for with blank. What's the equivalent for a list?
Try this:
|configure|processor|add operator|parsequotedstring|
|check|myemptylist|""|
Please see here: https://github.com/imanushin/NetRunner/wiki/Collection-result
If there will be any collection on output NetRunner (and fitSharp) will write several red rows (e.g. test will fail).
For example, by using such functions I'm checking the error entries in the log: the expected entries count is empty list. However, any problematic row will be presented to me on the web page, which is very useful: I collects log results from the several files and story them in the test.
In a view file I have:
= link_to 'View', post
= link_to 'View', comment
In a spec file (I'm using Capybara):
click_on 'View'
It clicks on the first link, but I want it to click on the second one. How can I do it?
You could try to find all entries and deal with an array:
page.all('a')[1].click
Would help to have a class or use within to scope your search ;)
There's probably a few ways but I usually scope something like this.
within(".comment") do
click_on("View")
end
There's quite possibly/probably alternatives as well. I usually do my acceptance testing from cucumber, so my steps typically look like
When I follow "View" within the comment element
Where I have a step that translates within the comment element to a scoped call to the step itself (which I think is built into the latest capybara web_steps)
The worst thing about "the second" link is that it can become the third or the first or even the twenty fifth someday. So, scoping with a within block is the best way. Example:
within(".comment") do
click_on("View")
end
But if it is difficult to specify the link with a within scope (which sometimes it really is), I guess the way to click the second link with a certain text is:
find(:xpath, "(//a[text()='View'])[2]").click
In later versions of capybara (2.0.2, for example) both click_on 'View' and click_link 'View' will raise an ambiguous match error:
Failure/Error: click_on 'View'
Capybara::Ambiguous:
Ambiguous match, found 2 elements matching link or button "View"
So this won't do even if you want to click the first link (or if any link would be ok, which is my case).
As far as I understand this is made to force people write more specific tests where particular links are clicked.
It definitely could be tricky to debug the code if you accidentally placed two or more links with identical text and try to see what is happening. It's good to rely on something that is unlikely to change and specifying a link with a within block is a nice way to do this.
There are many ways for solving this type of problems.
Do it like this
if(page.find("a")[:href] == "comment")
click_on("View")
or
page.find("a:eq(2)").click
Remember javascript indexing starts with 0 while In Capybara, indexing starts with 1. So use a:eq(2) here for second href.
For capybara 2 solution:
within(".comment") do
click_on("View")
end
would not help if you have a few .comment. So simple use: page.first(:link, "View").click
This works for me if you have several rows of identical classes and you want to find the second row. Like a previous author mentioned, capybara indexing starts at 1.
within all(".trip-row")[2] do
assert page.has_content?("content")
end
If you use capybara-ui you could define the widget, or reusable DOM reference, for each widget.
# define your widget. in this case,
# we're defining it in a role
widget :view_post, ['.post', text: 'View']
widget :view_comment, ['.comment', text: 'View']
# then click that widget in the test
role.click :view_post
role.click :view_comment
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.