How to handle weird behaviours with jQuery application using Selenium? - javascript

I am working on a stabilization of an old application which is developed using jQuery. In order to stabilize it, I added a method which, using javascript, checks for document is ready document.readyState === "complete"and ajax calls have been completed jQuery.active == 0 before interacting with elements.
But I still see following kind of issues -
Even though element is present on a page, Selenium WebDriver unable to find it even with polling mechanism which has some timeout set. However we can find same element, by checking in DOM.
UI is distorted and when WebDriver tries to click on an element, it gives error Element click intercepted. However same element can be clicked manually.
Even application has jQuery, when we execute javascrit jQuery.active == 0 through WebDriver, it results into jQuery is not defined. However if we execute same query through browser console, it returns correct result.
For all above, when I said something worked that means I've checked that in same browser instance which WebDriver had launched.
Could you please help how this can be resolved? I have been struggling for a week to stabilize this.

Related

Is there a way to transform a page loaded by HtmlUnit before it starts executing javascript?

First I would like to describe the motivation for my question.
I have a complex web page to test with Selenium + HtmlUnit, which launches diverse javascript scripts. The problem which I describe should be quite common.
On the page there is a button to which jQuery binds a click callback (click event handler) after the page is loaded. There is an explicit Wait (this is a Selenium term) for the button to become clickable in the test code. So as soon as the button becomes clickable, it gets clicked by Selenium. Often, however, this happens before jQuery manages to attach to the button the click event handler. In this case the Selenium test fails.
What I thought to do is to preprocess the web page accessed by HtmlUnit before javascript starts executing on the page, injecting some <script>myownscript()</script>at the beginning of the page (so that it executes before any other script on that page). Then I would be able to know, controlling certain conditions in the Selenium test code, when exactly the attaching of the click event handler has happened (how I exactly do this, depends on the details of the application). If I make Selenium click the button then, the presence of the click event handler will be guaranteed, and the test would proceed further as planned - with no errors due to the missing click event handler.
Let us leave apart the question whether the idea is a good or a bad one (a much simpler one, of course, would be just introducing a large enough delay in the Selenium test code before trying to click the problematic button, but then there might be a problem with the overall duration of tests, because the problem I described is present on many pages of the application being tested).
Are there some hooks in Selenium/HtmlUnit which permit to preprocess the page fetched from the server, injecting a script as I described, before javascript starts executing on the page?
In this case, you can use JavaScriptExecutor. You can add a function to do anything you want in the String script.
WebElement button = driver.findElement(By.id("my-button"));
JavascriptExecutor jsExe = ((JavascriptExecutor) driver);
String script = "console.log(arguments[0].id); return arguments[0].id";
Object oj = jsExe.executeScript(script, button);
String txt = oj.toString();
System.out.println(txt);
Please be careful if you want to use aycn such as setTimeout(), it will return immediately. See an example for async method in my answer at: method execute_script don't wait end of script to return value with selenium in python

Executing function in python after page timeout using selenium API

I have a python script that is utilizing selenium with the chrome webdriver. When I load pages sometimes it'll get stuck because some javascript is trying to load. If I run the line:
driver.execute_script("$(window.stop())")
that occasionally (but not reliably) works to simulate the stop browser action. My other thought was to change the page_load_timeout to 5s. That will throw an error when this annoying javascript isn't loading. I want to stop the page from loading with the timeout, but then run the function that I initially wanted to run... How do I accomplish that?
One way is, you have to assert by inspection which element could do the delay and with the element's #id you you can wait for that element to load with a time out.
try:
WebDriverWait(driver, 60).until((EC.visibility_of_element_located(By.Id, 'id')))
except TimeoutException:
pass
Or you can use the staleness_of approach, explained in related SO answers,
#contextlib.contextmanager
def wait_for_page_load(self, timeout=30):
old_page = self.find_element_by_tag_name('html')
yield
WebDriverWait(self, timeout).until(staleness_of(old_page))

Capybara with js: true not finding checkbox

I need to test if Javascript alert is being displayed in validation proccess after checking one checkbox.
I'm using RSpec, Capybara with Webkit and Database Cleaner.
My test without JS: true :
it "alerts to choose two players" do
set_and_visit
first("input[type='checkbox']").set(true)
expect(page).to have_content('Remember that you must choose two players to start a game.')
end
Finds a checkbox but cannot find the alert message.
When I add JS - "alerts to choose two players", js: true do it returns an error
NoMethodError:
undefined method `set' for nil:NilClass
I've tried using check 'John Doe' and first("input[type='checkbox']").check but it still didn't work.
There are two possible issues that seem likely:
Asynchronous Issues
The default driver (rack-test) is synchronous, so the page will always be fully loaded by the time you run first. When using asynchronous drivers like capybara-webkit, you need to watch out for race conditions. The page may still be loading (or updating via Ajax) when first is called, so the input element may not have appeared yet.
Solution: use find("input[type='checkbox']").set(true) instead of first. This will cause Capybara to wait up to two seconds for the input to appear before giving up and raising an error, instead of immediately returning nil.
I wrote more about writing asynchronous tests in a blog post.
Hidden Elements
Because the default driver (rack-test) doesn't parse CSS or run JavaScript, it doesn't understand which elements are hidden or visible. This means that you can interact with hidden elements that real users can't click on or fill in.
On the other hand, drivers like capybara-webkit know which elements are actually visible to the user. By default, Capybara ignores all elements that it knows are hidden.
Solution: use find("input[type='checkbox']", visible: false).set(true)
Note that this won't work if the element is actually visible, and that it's probably better to figure out why the element is hidden. After all, if capybara-webkit can't find it, your users probably can't, either.
You can read more about how Capybara handles visibility on the elabs blog.
So, silly as it sounds the mistake I made was in a url - in my set_and_visit method I was visiting 'matches/new' instead of '/matches/new'. When tested, puts page.body was returning an empty string because it couldn't find that url.
Still I'm not sure why test including check 'John Doe' wasn't returning any error like the one with js: true.

Is there any DOM event that occurs when a page completes its change?

I'd like to do things like this;
elem1 = document.getElementById(id1);
elem1.click()
// The page has been changed
elem2 = document.getElementById(id2);
elem2.click()
My problem is, if the second 'getElementById(id2)' is called before the HTML of document.documentElement completes its change, it sometimes fails to get elem2 because there is no HTML element that has id2. (Every javascript sentence in the above code is sent from 3rd party like Selenium Server, and there is a javascript loop that gets the sentence and runs it one by one. You can think the loop as a thing like Selenium Core)
I'm trying to write a tool like Selenium or Watir. So I can't use those tools. I think Selenium or Watir has a way to send the second command after it waits until the page has been completely changed as the result of the first command. I tried events like window.onwebkittransitionend because I'm trying this on Webkit, but it did not work.
I don't think promise or async can solve this problem, because it needs more than enforcing sequences. It needs to verify if the second page is fully loaded, because the second command can be the others like fetching the information of rendered images rather than just click.
Is there any event or a way to check if page change is completed? Oh, and I can't use jQuery unfortunately. I need to do it with plain Javascript.
Thanks for reading my question.

WebDriver: Change event not firing

I have an application that is using KnockoutJS and I'm attempting to write some tests that test a form. If you don't know KnockoutJS, the short story for it is that it provides bindings from my view to my data model. This means that when I type a value in the an input field, my underlying object automatically gets updated with that input fields value. This is done through a change event by default.
The problem I am having is that when my WebDriver test is typing into the field, the change event is not firing so my underlying data model does not have the appropriate values. This causes my form validation to fail when it should not.
I've done everything I could find on the internet to make this work. I've:
sent the tab key
clicked away from the form field
send JavaScript code to fire focus and blur events (validation occurs on blur)
clicked the form field before typing
set waits just incase it was a timing issue
changed KnockoutJS to update input field on afterkeydown
None of these have worked for me.
In addition, I have verified that this is not an event bubbling issue as I removed all other events explicitly, leaving just the KnockoutJS change event.
for the solution i'm looking for is one that works for all browser drivers (... at least the main ones e.g. IE, FF, Chrome, Safari) and does not require the use of jQuery.
How do I solve the problem?
Here is the relevant code I'm using to type values into the field:
// find element
WebElement input = this.element.findElement(By.className("controls"))
.findElement(By.tagName("input"));
// to set focus?
input.click();
// erase any existing value (because clear does not send any events
for (int i = 0; i < input.getAttribute("value").length(); i++) {
input.sendKeys(Keys.BACK_SPACE);
}
// type in value
input.sendKeys(text);
// to fire change & blur? (doesnt fire change)
//input.sendKeys(Keys.TAB);
// to fire change & blur? (doesnt fire change)
driver.findElement(By.tagName("body")).click();
So I have found a way to work around this issue for now, but by far do I believe this is the correct solution. This does break my rule about not using jQuery, however I feel this is okay for me as KnockoutJS requires jQuery be loaded. There's probably a plain ol' JavaScript way of doing this. I have tested this with FireFox, Safari, and PhantomJS. I assume it will work just as well in Chrome. I give no promises for Internet Explorer.
I am NOT going to mark this answer as the correct answer. The correct solution should be through WebDriver browsers firing the proper events. Only when I believe this is not possible through WebDriver will I mark this as the correct answer.
// find element in question
WebElement input = this.element.findElement(By.className("controls"))
.findElement(By.tagName("input"));
// click it to fire focus event
input.click();
// erase any existing value
for (int i = 0; i < input.getAttribute("value").length(); i++) {
input.sendKeys(Keys.BACK_SPACE);
}
// enter in desired text
input.sendKeys(text);
// fire on change event (WebDriver SHOULD DO THIS)
((JavascriptExecutor) driver).executeScript(
"$(arguments[0]).change(); return true;"
, input);
// click away to fire blur event
driver.findElement(By.tagName("body")).click();
So I believe I have found out my problem. I will fully admit this was PEBKAC. I had forgotten that WebDriver has issues if the browser window is not in active focus on the machine (which is still weird to me). When I was debugging the problem, I was using my editor to step through the code. By running the code normally and without removing focus from the browser, the events fire as expected using all three solutions (tab, click-away, and javascript).
I have an example project showing all three methods, however I am a major noob with git and github and am having problems delivering the project. Once i figure that out, I will share the project with you all.
EDIT: Got the example code up on GitHub (https://github.com/loesak/knockout.webdriver.change.event.test). You can either start the project as a webapp in a server and run the test manually or you can run the tests via maven (mvn clean install). I didn't put a whole lot of effort into getting this to work robustly so it is assuming you have Firefox installed and port 0808 8080 is open.
EDIT: Fixed stated port number

Categories

Resources