autocomplete method refuses to fire in RSpec test - ruby-on-rails-3

I am on Day 4 of trying to get an autocomplete field to fire in an RSpec test. Works super in the browser, it is just incredibly resistant to running in my request specs.
UPDATE: It looks like my RSpec/Capy scripts are running against the dev db instead of the test db. I'm using Pow, so I don't know what to set default_url_options[:host] or Capybara.app_host and Capybara.server_port to. I have a feeling if I fix this, it may work.
The stack is:
Rails 3.2.16
Capybara
RSpec
Poltergeist/PhantomJS
Pow
Zeus
Factory Girl
Click links, click buttons, fill_in fields all work great. But when it comes time to get this autocomplete to work, it absolutely refuses to work.
I am using this method:
def fill_autocomplete(field, options = {})
fill_in field, with: options[:with]
page.execute_script %Q{ $("##{field}").trigger('focus') }
page.execute_script %Q{ $("##{field}").trigger('keydown') }
selector = %Q{ul.ui-autocomplete li.ui-menu-item a:contains("#{options[:select]}")}
Capybara::Screenshot.screenshot_and_open_image
page.should have_selector('ul.ui-autocomplete li.ui-menu-item a')
page.execute_script %Q{ $("#{selector}").trigger('mouseenter').click() }
end
which I found here. The screenshot line is my own. But the line above:
page.should have_selector('ul.ui-autocomplete li.ui-menu-item a')
returns false. It works like a charm in the browser. I just can't for the life of me figure out why it won't work. I have tried everything I know how. How can I debug this?
The screenshot just shows the page I am expecting, with the field filled in appropriately. I even tested this with a "hello" alert that I inserted into the autocomplete call. Works flawlessly in the browser, but no result at all in the test.
In short, it looks like the following two lines are having no effect:
page.execute_script %Q{ $("##{field}").trigger('focus') }
page.execute_script %Q{ $("##{field}").trigger('keydown') }

I had a similar problem and even though Capybara's documentation says that have_selector will wait for the AJAX call to complete, it did not work for me.
The following worked in my case:
def fill_in_autocomplete(field_label, field_class, options = {})
field_id = "##{page.evaluate_script("$('#{field_class}').attr('id')")}"
selector = "ul.ui-autocomplete li.ui-menu-item a"
fill_in(field_label, with: options[:with])
page.execute_script("$('#{field_id}').trigger('focus')")
page.execute_script("$('#{field_id}').trigger('keydown')")
sleep(2) # Hack! not sure why the line below isn't working...
#expect(page).to have_selector(selector, text: options[:with])
page.execute_script("$('#{selector}').click()")
end
You can call the method above like this:
fill_in_autocomplete('Some label', '.js-some-field', with: 'Some value'
I didn't want to pass the field ID and opted for a class instead, which is why the first line in the helper gets the ID based on the element's class.

This kind of asynchronous call is really tricky to troubleshoot. As Xaid suggested, sometimes just putting in an arbitrary sleep suffices to get the job done.
I've found in my own coding that what typically happening is that though Capybara tries to intelligently wait for the AJAX to do its magic, something is causing Capybara to believe the conditions are met for it to continue when you're not actually ready for it to do so.
Digging into the code for Capybara, you can find that the delays around asynchronous behavior are accomplished with the synchronize wrapper function that's part of the Node::Base class. (See here, if you're curious how it actually works: https://github.com/jnicklas/capybara/blob/master/lib/capybara/node/base.rb#L73)
Essentially, it's doing a series of wait commands for you (0.05 second each time) and trapping any "I can't find that element" exceptions until the timeout (defaulted at 2 seconds) has passed. If for any reason, the element you're waiting on DOES exist before you expect it to, it will stop waiting and move on.
Dumping out the HTML of the page as it exists in the headless browser can be a great troubleshooting trick. Try inserting a puts page.body into your code before your screenshot code. That should output the HTML for the page. Does the code include the ul.ui-autocomplete li.ui-menu-item a where you expect it to be? How about after that delay Xiad suggested. Does it exist then?
For really sticky problems, I'll add the pry gem to my project and insert a binding.pry into the code. That lets me play with the browser from within the test to see what it's seeing. Granted, with timing issues, I typically can't act fast enough to see what it's seeing, but even that's a clue.
Hopefully, these troubleshooting tips are helpful.

I had this problem and no proposed solution could solve it. My tests always failed when trying to find the ul.ui-autocomplete element. I finally noticed, that jQuery autocomplete appends the ul to the end of the html page and NOT to the input field in question. In my spec, I follow the practice of targeting my forms explicitly by within my_form do and doing all the fill_instuff inside this block:
within my_form do
fill_autocomplete …
end
Of course this could never find the ul attached OUTSIDE this form element. My solution was simple: Use jQuery autocomplete's attribute appendTo: '#id_of_input_field' when initializing autocomplete. Now it can find my uland everything works fine.

Related

Capybara: page.execute_script() not working

I am trying to simulate drag and drop using jquery.simulate library in rspec feature.The execute_script lines in spec are:
page.execute_script("$('#slide_1').draggable();")
page.evaluate_script("$('#slide_1').simulate('drag', {dragTarget: '#library_swap', interpolation: {stepWidth: 10, stepDelay: 300 }});")
page.evaluate_script("$('#slide_1').simulate('drop');")
If I run the lines inside the execute script over chrome console,its working fine(drag-drop simulation works),but not working with execute_script
Since you're not getting any errors, the JS code you're passing to execute_script is actually being executed. Since you're not seeing the behavior you expect the most likely explanation is that you're executing the JS before the element is actually on the page, which would then just silently do nothing. The one thing that confuses me about the code is why you're calling draggable on the #slide_1 element since I would assume that had already been called in your app. Anyway - add an expectation before your execute_script calls to make sure the element is actually on the page
expect(page).to have_css('#slide_1')
execute_script("$('#slide_1')...
Also note, there shouldn't be any reason you need to use three different execute_script calls for this, you could just combine them all into one. In recent versions of Capybara you can also DRY up the commands by not specifying the selector again and instead passing the element to execute_script
slider = page.find(:css, '#slide_1')
execute_script("$(arguments[0]). ... ", slider)

Codeception ElementNotVisibleException error, unable to select option, or click

I am unable to interact with an element using browser tests. It says the element is not interact-able, or not visible. This doesn't happen in Acceptance
Sometimes this solution doesn't work because the element is unavailable for some other cryptic reason.
We just had a situation where we couldn't use a <select> element to pick one of the options.
Further more, there was behaviour that was being triggered by the "change" event when the option was selected.
We were able to solve it like this.
$js = "jQuery('#chosen-option-quantity-2').val('2').trigger('change');";
$I->executeJS($js);
so the first command selects the option, and the second triggers the change event.
I hope that helps some one, even if it is me in the future.
The problem that is happening here is that the html element is being hidden by something, probably css somewhere. Because it is hidden (display:none), WebDriver can't see it, and therefore can't interact with it.
In order to fix this problem, you need to use JS to un-hide the element.
use this $I->executeJS('jQuery("#your-css-selector").show()');
This doesn't happen in Acceptance tests because PHP Browser looks at the Page Source, and so can see everything, while WebDriver see's what a user see's on the browser.
You may use PhpBrowser
It works only with HTML then how PhantomJs emulate the real browser
But, with PhpBrowser you can't see what see your browser (only HTML such I said)
Another way, try executeJs with PhantomJs as it said before

PhantomJS: injecting a script before any other scripts run

Using PhantomJS, I'd like to inject some JS as if there was an extra <script> tag before any other <script> tags. This is because the scripts on the page use some functions that PhantomJS does not have, namely Function.prototype.bind and window.webkitRequestAnimationFrame. I have a JS file with custom implementations of the two and I'd like PhantomJS to use them when running the scripts on the page.
The difficulty is that if I do page.injectJs before page.open, the script is injected into an empty page and is not carried over to the page being opened.
Alternatively, if I do page.injectJs after page.open, it's too late as the JavaScript errors (undefined functions) have already occurred.
I've found a way that appears to work, but is obviously a hack:
page.onResourceReceived = function() {
page.injectJs('phantom-hacks.js')
};
This injects it many times (twice for each resource, apparently), but that's okay because my script is idempotent. However, I'd like to know the proper way to do this: inject it only once and before any scripts on the page are run.
Thanks :)
I don't think there's a "proper" way to inject such script other than hooking to events.
I've spent half a year working massively with PhantomJs and found no way to inject before all the errors start happening but after the page finished loading.
I would try to go through onInitialized, onLoadStarted, onLoadFinished. Inside the hooks I would call to page.evaluate() which would just modify DOM to have this extra whatever place you like.
I think one of them (the hooks) should give you the right timing you want.
Cheers

selenium webdriver is clearing out fields after sendKeys had previously populated them

The webpage that I'm testing is using knockout. On other pages on our site that are not currently using knockout I'm not having the same problem. The scenario I have is where the page opens, I enter in various required fields and click the save button. At some point between when it enters a value in the last text field and when it clicks the save button the fields that previously had values become cleared out, and thus the script can't continue. Here is an example of the code that I'm running:
driver.findElement(By.id("sku")).clear();
driver.findElement(By.id("sku")).sendKeys(itemNo);
driver.findElement(By.id("desktopThankYouPage")).clear();
driver.findElement(By.id("desktopThankYouPage")).sendKeys(downloadUrl);
driver.findElement(By.id("mobileThankYouPage")).clear();
driver.findElement(By.id("mobileThankYouPage")).sendKeys(mobileDownloadUrl);
driver.findElement(By.id("initialPrice")).clear();
driver.findElement(By.id("initialPrice")).sendKeys(initialPrice);
driver.findElement(By.id("submitSiteChanges")).click();
Like I said, between the time it enters text in the last field and the time it clicks save the fields that previously had text in them get cleared out, and thus my test fails. The problem is it doesn't always happen. Sometimes the test runs fine, other times it doesn't.
I've tried putting Thread.sleep(x); all over the place to see if pausing at certain points would fix the problem. I also have tried using javascript to wait in the background for any ajax that might be running. Also have the implicit wait of driver.manage().timeouts().implicitlyWait(10, TimeUnit.SECONDS). None of it seemingly has made any difference.
I'm running version 2.13 of selenium server and all my tests run on Firefox 8.
Any help on this would be greatly appreciated!
Firefox has a bug which prevents some events from being executed while the browser window is out of focus. This could be an issue when you're running your automation tests - which might be typing even if the window is out of focus.
The point is that knockout model updates are triggered (by default) with the change event. If it's not being executed, it's underlying model won't be up-to-date.
To fix this issue I triggered the change event "manually", injecting javascript into my tests.:
//suppose "element" is an input field
element.sendKeys("value");
JavascriptExecutor jsExecutor = (JavascriptExecutor) driver;
jsExecutor.executeScript("$(arguments[0]).change();", element);
As you might have noticed, I'm using jQuery to trigger the change event. If you're not using jQuery on your app, you can check here how to trigger it using vanilla javascript.
Hope that helps somebody.
I had the exact same problem. I would guess also that your code works fine in Chrome but not firefox, and that it always works when you do it manually?
Anyway, the problem is likely to be that Selenium doesn't really behave the same way as a real user, and doesnt trigger the same events. When you "submit" the form, it sometimes won't have executed the "change" event on a text area, meaning that it won't have changed.
I had the same problem testing Backbone.modelbinding, which uses the "change" event to update the model from the form. Knockout also uses the "change" event, but fortunately it can also use the "keyup" event. See valueUpdate in the docs:
<input data-bind="value: someValue, valueUpdate: 'keyup'" />
Anyway, that reproducibly solved it for me, and didnt need any sleeps once I had that done. The downside is that you'd be running the event more than is necessary in production, in order to make tests work. Another downside is that you if you want to run some code when a value changes, you'll now get one event per keypress instead of one per field change, which sucks sometimes.
There is another solution, which is to make Selenium fire the change event yourself, for example: Selenium IE change event not fired. It's also suboptimal, but what can you do.
You could also try putting the focus on a button before you click it. Don't know if that will work, I haven't tried it.
I was facing the same issue, while using JavaScriptExecutor for sending keys to text fields.
Using below code in IE (same code is working with chrome):
(JavascriptExecutor) driver.executeScript("arguments[0].value = '" + value + "';", element);
After updating the code to simple "sendKeys()" method, it resolved my issue:
element.sendKeys("some text");

How to get WatiN to scan dynamically loaded content?

The situation is that I have a page that uses some AJAX calls to retrieve content from the server, then puts those results into a chunk of html generated by another script. The problem is, I can't select with watin any of the elements of this new piece of html. It can be viewed in the browser, and comes up when I hit F12 and scan through the code, but still WatiN can't see it.
Is this because WatiN only scans through the html source of the page, and not the current version of the HTML? I think a similar situation would be:
html -
<script type="text/javascript">
$('#foo').html("gak");
</script>
...
<div id="foo">bar</div>
then when I try and assert -
Assert.IsTrue(browser.Div("foo")).ContainsText("gak"));
it will return false.
Any ideas on this? or is my best option to just write a bunch of jQuery, and browser.Eval() it?
I test AJAX pages quite a bit. The key is to wait until the asnyc postback has completed. If you have
Assert.IsFalse(browser.Div("foo")).ContainsText("gak");
browser_action_that_changes_bar_to_gak
>> Here you need to wait <<
Assert.IsTrue(browser.Div("foo")).ContainsText("gak");
In the "wait" section you can do a System.Threading.Thread.Sleep(numberOfMilliseconds) <- this is not the best way, but it is really simple. Once you determine that waiting is what you need to do, a better way to wait is to poll the status rather than way numberOfMilliseconds each time. I believe different AJAX libraries do things differently, but what works for me is really similar to this: http://pushpontech.blogspot.com/2008/04/ajax-issues-with-watin.html
I put the JavaScript into an Eval() in a helper function in the my base Page class rather than having to inject it into every page like the article did.
.
my Base Page class contains:
public bool IsInAsyncPostBack()
{
const string isAsyncPostBackScript = "Sys.WebForms.PageRequestManager.getInstance().get_isInAsyncPostBack()";
return bool.Parse(this.Document.Eval(isAsyncPostBackScript));
}
And then my WaitForAsyncPostback is basically the same as in the linked post, but I added a max wait time. Before going to Page classes (awesome; do it!) I made these static functions somewhere else and it worked too.
This is almost surely a timing issue. The jQuery has not updated when you test. Rather than introducing any artificial pause or wait it's best to wait for something to show that your AJAX has worked as expected.
In this case a WaitUntil should do the job nicely:
Assert.IsTrue(browser.Div("foo")).WaitUntil(c => c.Text.Contains("gak")));
This works for most updates and the like. Another common waiting pattern is on data loading say, where you'd have a spinning wheel displayed. Then you could wait until this wheel is gone with a something like:
WaitUntil(c => c.Style.Display == "none");