Capybara integration testing with asynchronous JavaScript - javascript

I have a Rails integration test that's failing, and I can't figure out why. I'm using Capybara with Selenium as the driver.
The test checks that page content has been removed after an AJAX call takes place. The relevant action is that a button is clicked, and that button click causes a section of the page to be removed via a jQuery remove() call. Here's an approximation of the integration testing code:
click_button("Remove stuff")
assert has_no_link?("This should be removed")
The assertion fails, implying that the link still exists.
I've been reading up on Capybara, and I know that you can extend the default wait time. I've extended it to a ridiculous value (20 seconds), and still the assertion fails.
When I follow the test process myself manually, the source of the page still shows the content, but the DOM does not (by viewing Firefox's DOM Inspector and looking for the element). Is this the issue? I've even tried inspecting the DOM while the tests are running in Firefox to check if the content was there, and it doesn't appear to be.
I have no idea how Capybara is still finding this link that no longer exists in the DOM. Is Capybara examining the source instead of the DOM and finding the link there? If so, I have no idea how to fix this test to make sure that the test passes. Refreshing the page would fix the issue, but that's not exactly what a user would do, so I hesitate to change the page just to make the test pass...
Would love any recommendations on how to approach this problem.
Thanks!

Thoughtbot has a great blog post on waiting for AJAX, which you can read here, though it is based on Rspec, and it looks like you are using TestUnit.
It works great for situations when Capybara doesn't quite wait long enough, but doesn't add unnecessarily long timeouts. I work mostly in Rspec now, but I think you can modify it by doing this:
# Create this file in test/support/wait_for_ajax.rb
module WaitForAjax
def wait_for_ajax
Timeout.timeout(Capybara.default_max_wait_time) do
loop until finished_all_ajax_requests?
end
end
def finished_all_ajax_requests?
page.evaluate_script('jQuery.active').zero?
end
end
You can either include it when needed in the individual test file, or use one of the strategies provided in this SO post for automatically including it every time.
Then, whenever you have a test that is not properly waiting for AJAX to finish, just insert the line wait_for_ajax. Using your code as an example:
click_button("Remove stuff")
wait_for_ajax
assert has_no_link?("This should be removed")

there was some method called wait_until, but it was deprecated recently and changed with synchronize method.
http://www.elabs.se/blog/53-why-wait_until-was-removed-from-capybara
https://github.com/jnicklas/capybara/blob/master/lib/capybara/node/base.rb#L44
For now I don't know how to use it exactly, but I'm waiting for answer for my question from the author, so I hope to resolve this problem soonly

There's a neat way to check that ajax requests are done, which I learned from this article. Instead of wait with some specific time, you can use the ajax $.active function (which is not in the actual API but is exposed so you can use it). $.active tells you the number of active connections to a server, so when it drops to zero you know the ajax request is done:
wait_until do
page.evaluate_script('$.active') == 0
end
If that doesn't work, then the issue is somewhere else (which judging from what you wrote seems likely). If the change is only happening in the DOM, then you have to make sure that javascript is enabled for your test/spec. In rspec, for example, you set :js => true to do that; in Cucumber you add a line above the scenario with #javascript. I don't use rails' default tests but there must be a setting to do the same.

Are you first testing something else with the link, and then testing that it is removed?
In other words, is your test something like:
has_no_link = $('#id_for_link')
//.... some test
click_button("Remove stuff")
assert has_no_link?("This should be removed")
If that's the case, then has_no_link will still point to the link. remove() will remove it from the DOM, but your variable still points to it in memory.
You should query for the link again in the DOM to see if you get a result.

I had to rewrite wait_until for a test that involved waiting for a callback triggered by streaming a youtube video to a certain point. Here's what I used:
# in spec_helper.rb
require "timeout"
def wait_until time=0.1
Timeout.timeout(Capybara.default_wait_time) do
sleep(time) until value = yield
value
end
end

Couple of approaches I thought of:
1: Check your capybara version and look for any bugs with your version
2: Maybe try doing a find on the link after the button is clicked
click_button("Remove stuff")
find(:xpath, '//a[text()='Link should be removed').should be_false
3: Use has_link? instead of has_no_link?
click_button("Remove stuff")
page.has_link?("Link should be removed").should be_false

You can always do this manually. Using #shioyama's idea:
def wait_for_ajax
timer_end = Time.now + 5.seconds
while page.evaluate_script('$.active') != 0
if Time.now > timer_end
fail "Page took more than 5 seconds to load via ajax"
end
sleep 0.1
end
end

Related

How to wait for PyScript to be fully initialised (environment included)?

Good morning,
I would like to wait for a message (logged to my browser's console by a script I imported in my HTML tag) to be logged and then do an action in JavaScript.
For example, I'd like to wait for "ABCD:READY" to be printed in the console to use a function called finallyStart().
Is it possible ?
I thought I only needed to read the content of stdout to do this, but it looks like browsers don't call their console output stdout...
I also tried to find an answer in stackoverflow, but none of the posts I found were similar to the one I'm currently writing.
I'm a novice when it comes to JS in browsers (I've only used NodeJS for back-end), so thanks in advance for your help !
I faced the same problem and eventually came up with this hacky but working solution.
First create a dummy element in your html.
<div hidden id="start"></div>
Make an event for that element call a function in your js script.
document.getElementById("start").onclick = main;
Then trigger that event manually from your python script.
from pyscript import Element
Element("start").element.click()
No python code can execute until pyscript has finished initializing, so this seems like a reliable strategy. It would be even better if you could call the main() function directly from your python script, but i didn't have the patience to figure that out.
p.s. For some reason it takes a few seconds after pyscript has officially initialized for the code to kick off (at least in my test). I have no idea what's happening during that time, though i suppose it doesn't make a big difference to the already hefty build time.

Cypress How to handle erors

I'm testing a lot of things but some of them are not too important(like caption text fail)I want to add optional parameter (if its wrong thats okay continue testing)
I used to work with Katalon Studio, it has Change failure options(stop,fail,continue) Can I make it with Cypress for my test cases.
Sample image
As Mikkel mentioned already Cypress doesn't like optional testing. There is a way how you could do that by using an if-statement as explained in this question: In Cypress, is there a way to avoid a failure depending on a daily message?
But to do that for every test you optionally want to test can make a big pile up of your code. So if you don't care if it succeeds or fails, just don't test it.
Another way you can try to be more resilient is by cutting up the tests further more. But you have to make sure that the scenarios don't rely on each other otherwise they will still fail.

Keep and run code in debugger every time the page is reloaded

I have a function that will reload the current page after a period of time. I want this function to run automatically every time the page is reloaded (using the debugger).
function reloadPage() {
window.location.reload(false);
}
setInterval(reloadPage,3000)
The problem is that every time the page is reloaded, the code in the debugger will be cleaned and the function will not be called. How to fix this?
A very simple solution: Rather than putting the Javascript into the console, consider putting it in your application but disabling it when you're not debugging.
For example, you could have a GET parameter in your URL that, when present, triggers the function. A good explanation of how to retrieve a GET parameter in Javascript is at How to retrieve GET parameters from javascript?
An even simpler alternative would be to simply leave this code commented out, and comment it in when you want to debug. (This is not a good practice and I will scold you for it during code review, but it is a real thing that real people do, and it has the advantage of being easy and working.)
-
An alternative: You could detect when the console is open, and only run your code when the console is detected (though this would annoy power users like me who tend to always have developer tools open). It's not trivial to detect, but there's a library you can use: https://github.com/zswang/jdetects

Passing a location.search parameter to a Jasmine (Karma) test

I have a javascript file that uses location.search for some logic. I want to test it using Karma. When I simply set the location (window.location.search = 'param=value' in the test), Karma complains I'm doing a full page reload. How do I pass a search parameter to my test?
Without seeing some code it's a little tricky to know what you exactly want, however it sounds like you want some sort of fixture/mock capability added to your tests. If you check out this other answer to a very similar problem you will see that it tells you to keep the test as a "unit".
Similar post with Answer
What this means is that we're not really concerned with testing the Window object, we'll assume Chrome or Firefox manufacturers will do this just fine for us. In your test you will be able to check and respond to your mock object and investigate that according to your logic.
When running in live code - as shown - the final step of actually handing over the location is dealt with by the browser.
In other words you are just checking your location setting logic and no other functionality. I hope this can work for you.

Calling a function in a JavaScript file with Selenium IDE

So, I'm running these Selenium IDE tests against a site I'm working on. Everything about the tests themselves is running fine, except I would like to do a bit of clean-up once I'm done. In my MVC3 Razor based site, I have a JavaScript file with a function that gets a JsonResult from a Controller of mine. That Controller handles the database clean-up that Selenium IDE otherwise couldn't handle.
However, I'm having a hard time finding any sort of documentation on how to do this. I know I can do JavaScript{ myJavascriptGoesHere } as one of the Values for a line in the test, but I can't seem to find a way to tell it to go find my clean-up function.
Is it even possible for Selenium IDE to do this sort of thing?
If it comes down to it, I can just make a separate View to handle the clean-up, but I'd really like to avoid that if possible.
Thanks!
If you want to execute your own JavaScript function that exists in your test page from Selenium IDE, you need to make sure you access it via the window object. If you look at the reference for storeEval for instance, it says:
Note that, by default, the snippet will run in the context of the
"selenium" object itself, so this will refer to the Selenium object.
Use window to refer to the window of your application, e.g.
window.document.getElementById('foo')
So if you have your own function e.g. myFunc(). You need to refer to it as window.myFunc().
This can be very handy for exercising client-side validation without actually submitting the form, e.g. if you want to test a variety of invalid and valid form field values.
If you use runScript, that should already run in the window's context.
This works for me.
IJavaScriptExecutor js = driver as IJavaScriptExecutor;
string title = (string)js.ExecuteScript("myJavascriptGoesHere");
Make sure your javascript works first before using it here!
Actually to access your page javascript space, you need to get the real window of your page : this.browserbot.getUserWindow()
See this statement to get the jQuery entry point in your page (if it has jQuery of course ^^ )
https://stackoverflow.com/a/54887281/2143734

Categories

Resources