How to test javascript function? - ruby-on-rails-3

Is it possible to test existance of js function in page using Capybara? if so, can I make a call and check result?

Capybara is used to test the overall functionality of your web application by actually rendering the page. When you are using an engine that can execute Javascript (the default Rack::Test engine does not), you can use it to check that your page is indeed rendered correctly after executing the relevant Javascript. There are ways to test a function's existence by calling page.execute_script("typeof yourFunctionName == 'function'") (and only select engines support this) and check its value but know that Capybara it is not designed to be a Javascript test framework. You should use Jasmine for that.

Related

Using Selenium IDE for a test-case which includes backend call?

I add a new category in the admin panel and want to ensure that the category is available in the dropdown on the user's part of the website. Recorded test in the Selenium IDE works fine. But the thing is, the task that I execute is of course not a pure frontend thing - the category is saved in the database and is loaded from it to show it to the user. So if something goes wrong on the database-side, the test will fail.
My question is: is it bad practice to do such tests that depend on backend-behavior ? Should I go for Selenium Webdriver ?
If you use Selenium Webdriver, your test will not change in a main thing. It still will check database side. Selenium Webdriver is just anouther tool for testing that is more flexible and allows to make more complex test then in Selenium IDE.
I don't think that it is bad practice, because it is just one of the tests that chould be executed to enshure you that this part of your project works correctly. In this case I would check back-end part(get all categories from DB or admin's panel and check that there is no extra or missing ones) and than check user's panel(all categories are the same as set in DB and admin's panel).

ID's Required for UI automation in selenium in all html pages

There is a project where we are going to automate the UI but the Automation team is suggesting that we have to use ID's all over the page so that it will be easy to automate there script.
My Question here is why we will use ID's everywhere ? hampering the Html and Css structure.
The webpage can be automated without ID's in html yes or no ?
Yes, a web page can be automated without ID's. For example, you can play with cssSelectors here https://www.w3schools.com/cssref/trysel.asp (note that example page has elements with and without ids)
Using ids for element's lookup in automation is generally considered as a best practice. If you use ids your automation tests will become independent of html structures which basically will make them more stable.
For example, in the first version of your app you may have some text implemented as
<p id="someTextId" class="someClass">Hello world</p>
but at some point may decide to rewrite it as (change the tag and even apply different class name)
<div id="someTextId" class="anotherClass">Hello world</div>
In case you rely on id #someTextId to locate an element your test will still be able to access necessary element and interact with it properly. If you use p or .someClass your automation test will fail to find an element even though from the ui perspective the same text will be displayed in a browser.
We faced several downsides of using id:
Some frameworks do not recommend using them or generate them automatically. (Potential issues with ids described here https://www.javascriptstuff.com/use-refs-not-ids/, https://www.quora.com/Are-IDs-a-big-no-no-in-the-CSS-world, https://dev.to/claireparker/reasons-not-to-use-ids-in-css-4ni4, https://www.creativebloq.com/css3/avoid-css-mistakes-10135080, https://www.reddit.com/r/webdev/comments/3ge2ma/why_some_people_dont_use_ids_at_all/)
Some other logic may rely on them, so changing/adding them for the need of automation may somehow affect other app logic unexpectedly.
What you can use instead of id is some other attribute. For example in our projects, we have switched from id to a specific attribute named dataSeleniumId. It clearly shows that the attribute is selenium tests usage only. As a next step, you can add a rule in your team when someone changes or removed dataSeleniumId attribute he should inform automation testing team about it. As changing/removing this attribute will lead to test failures and to avoid any false failures it is better to fix it in advance.
For an automation developer its much easier to browse trough the html code and see the id of specific button/text field/etc.. to implement the relevant locator inside the automated test.
In most cases, the project start to receive duplication of classes or complicated nested elements. This make the life of automation dev harder, because of writing xpath or css selectors, verify that they work and this locator finds only 1 unique element.
Its up to the team and code style suggested from the team leader.
Back on the question, yes the website can be written without id's but if the goals is to automate large part of the website, id's would be great helper to the automation dev team.

Sinatra Rspec - testing that a view has rendered

I am writing tests for a Sinatra app that takes input from an API via a gem. Once I have the API response I need to test that the template has correctly rendered. The response of the API will be the HTML of the page that I am loading.
My first instinct was to write a test that looks like this:
describe 'the root path'
it 'should render the index view' do
get '/'
expect(last_response).to render_template(:index)
end
end
Unfortunately when I try this I get the following error: undefined method `render_template'
I was wondering if anyone has encountered this problem - it seems like it should be an easy fix, but I can't seem to find any documentation to help with it.
I'm currently not testing views at all because of time constraints, but I did have some limited successs with Rack::Test.
In theory you can say:
require 'rack/test'
include Rack::Test::Methods
def app
Sinatra::Application
end
describe 'it should render the index view' do
get '/'
expect(last_response).to be_ok
expect(last_response.body).to eq(a_bunch_of_html_somehow)
end
If I were to go this road again, since my views are haml, I could implement the a_bunch_of_html_somehow method using a call to Haml::Engine -- but I'm not sure whether that helps you.
I'm lifting this wholesale from the Sinatra site here -- the page is well worth a read.
We ended up scrapping this approach since it was better handled by integration testing tool suites such as Selenium or Capybara. There is no equivalent that I could find in the basic Sinatra Rspec suite that could do this - it made more sense to move it into a different scope

how to interact with javascript JSON response with rspec and capybara

I'm using rspec and capybara and would like to test a JSON response that I get in response to for example click_button. I understand that I can use :js => true but I would like to parse back the JSON that I get. I see that I can do something like:
get '/your/path', format: 'js'
Is there a way to do something like:
click_button('Save', format: 'js')
::JSON.parse(response)....
?
thx
The aim of browser elumation libraries like Selenium is to approach real browsers and allow emulating behavior of real users. Real users can't read responses to AJAX actions so browser emulation libraries don't give such ability. Also it will be quiet hard for Selenium guys to implement it.
There are several possibilities that you have:
Write this test using one of http client libraries. But you can't really test end-to-end something that involves lots of Javascript and AJAX using this method
It's likely that AJAX action changes something in the DOM of your application. You can easily write a check for it using Capybara.
But if really want to test your app end-to-end and want to directly check HTTP response to AJAX action you should use a proxy that will record all responses. Then you make a search through those responses. Selenium maintainers advice to use Browsermob proxy to do something like this. Here are Ruby bindings for it that are written by a maintainer of Selenium's Ruby bindings
I had to do this for a special use case as well.
You can strip the html tags and parse it:
JSON.parse(ActionView::Base.full_sanitizer.sanitize(page.html))

Jasmine: testing my rails site's interface with it

I've been looking for ways to test a Rails 3 app that has quite a lot of JS code for its rich interface. I tried with Capybara, but that didn't work out, so now I'm giving Jasmine a try. But I'm having a hard time understanding how I should go about it.
From what I gathered, Jasmine alone is good for testing the JS components of a site, but what if I want to test the interface directly? I need something like:
describe "Sign in"
Visit '/home'
When user clicks "Sign in" link
The sign in form should appear
Can I actually do something like that with Jasmine? So far, my tests are included on a results page generated by the rails-jasmine gem and obviously they run over that DOM, not over my site's DOM.
I'm now trying to use evergreen, but I get the same result (I can't even include jQuery).
Any ideas?
Thanks!
To answer your question - no, Jasmine will not inherently function in a way which will let you navigate your site and test at such a high level, like an integration test with Cucumber.
Jasmine is built primarily to test the API of your js, and so essentially you will only be able to test URL routes or something with it if they are part of your API.
For example, you can test how a Backbone Router will respond to your window's current location, or navigating from one location to the next, but that is because Backbone Routers explicitly handle URL locations.
This might help: http://railscasts.com/episodes/275-how-i-test
You could try using jasminerice with fixtures. If the intent is to test the JS DOM interaction, then fixtures would be the way to go.
But if the idea is to test the server interactions as well, then going with something like cucumber makes sense.