Java Load Web Page and Keep Track of Changes in HTML - java

I'm trying to load the web page http://www.twitch.tv/NAME_OF_CHANNEL/chat?opentga=1 to keep track of a twitch chat through web scraping. The only problem is that whenever someone types a message in chat, a ul item is added to the html code. My question is, if I load the page with either Selenium or just an HTTP GET request, how can I keep getting the updated code so I can look out for all new chat messages that are sent into the chat?
This is what some of the code looks like.
As you can see there is a ul element that has a huge list of div elements with random ids. In each of the div elements there is the individual chat message, with certain information like what user sent it and at what time. The div elements are what keep getting updated, one being added every time a message is sent. How can I keep track of all the div elements saving each one in a list every time a message is sent? Thanks!

You can poll the DOM of your particular case.
The meaning of polling is to set the driver in a monitor state where it waits for some condition to be fulfilled.
You can have either implicit or explicit waiting.
Something like this would be a good head start
public static void main(String[] args) throws Exception {
WebDriver driver = new FirefoxDriver();
driver.get("http://www.twitch.tv/NAME_OF_CHANNEL/chat?opentga=1");
WebDriverWait initialWait = new WebDriverWait(driver, 60);
WebElement commentsContainer = initialWait.until(ExpectedConditions.presenceOfElementLocated(By.cssSelector("ul.chat-lines")));
if(commentsContainer == null)
throw new Exception("Page unresponsive!!!");
int numberOfComments = commentsContainer.findElements(By.cssSelector("div[id^=ember]")).size() + 1;
while(true) {
String newCommentSelector = "chat-lines > div:nth-child(" + numberOfComments + ")";
WebElement newComment = (new WebDriverWait(driver, 60))
.until(ExpectedConditions.presenceOfElementLocated(By.cssSelector(newCommentSelector)));
if(newComment == null) continue;
numberOfComments++;
System.out.println(newComment.getText());
}
}
This could be cleaned up. There might be errors, but the logic is straightforward.
You wait until you have the comments' container. Then you find all the comments present at that point and get their number. After that you just wait until you "see" the initial_number_of_comments + 1 comment.
The selectors might not be correct. Feel free to change them at will. This is a never ending poll loop, so you might want to introduce some kind of exit logic here.

Related

Selenium stop page load as soon as expected element exist

I created few test cases on Selenium using Java. Unfortunately when I click an element on the page, before I could move on to any other action, I have to wait till the page loads.
I have tried driver.manage().timeouts().implicitlyWait(30, TimeUnit.SECONDS);. Unfortunately this creates another problem. Even if the page loads, it waits 30 seconds before it started testing on the page.
What I found the best way is to send ESCAPE key to stop page load.
Is there anyway I could check if an element exists and when it does, send ESCAPE key to the browser to stop page load?
This part is bugging my mind as I have to wait till page loads before Java reads the next line of the code so I can't send ESCAPE key to browser till the page actually stops loading.
Edit
I have just tried using a new thread to do the job but it seems driver is completely locked out, can't do any process on it before page stops loading.
I'm out of ideas for the moment but I believe there should be a way.
Using timeouts() is causing whole test case to stop.
First I'd like to say this isn't a best practice. The selenium click method states that if the click triggers a page load, selenium will do its best to block until the page is loaded. Instead of clicking via the click method you could try sending the click event via JavaScript. Then wait for the element like normal.
You can try driver.manage().timeouts().pageLoadTimeout(10, TimeUnit.SECONDS); It is supposed to throw an error after timeout is over. I have never used it but maybe you can try and catch this error and continue with your test. But your page could end up in an unstable state with everything not loading and test interacts with elements.
I did it in C#, scenario is the same elsewhere.
Define driver like this:
var firefoxOptions = new FirefoxOptions();
firefoxOptions.PageLoadStrategy = PageLoadStrategy.None;
driver = new FirefoxDriver(firefoxOptions);
PageLoadStrategy.None means when open a URL, continue to next line regardless of the results and do not wait to load the page.
Usually, it takes some seconds to load a page and element appears, suppose I'm waiting for email_user element to appears:
int user_emailID = 0, popupAlert = 0;
do
{
float timeToWait = 0;
driver.Navigate().GoToUrl("https://stackoverflow.com");
do
{
await Task.Delay(500);
timeToWait += 0.5F;
user_emailID = driver.FindElements(By.XPath("//input[#id=\'user_email\']")).Count;
}
while (user_emailID == 0 && timeToWait < 10);
if (user_emailID == 1)
{
//Element exists now!do something and don't wait for page to load completely
}
}
while (user_emailID != 1);
More explanation: when open a URL, first loop check the presence of element every 0.5 second, if it appears, the loop stops. If after 10 seconds it couldn't find the element, the page will reloaded !!
Hope this get you the idea.
Remember, exception must not happen in your codes !!

element is not attached to the page document

While clicking on javascript components Im getting:
org.openqa.selenium.StaleElementReferenceException: stale element
reference: element is not attached to the page document
One way to fix it is to stop script for some short period of time with :
Thread.sleep(200);
I have my implicitlyWait set for 10 seconds, and I think there wasnt such problems with older selenium
But is there any other way to do it globally ?
driver.findElement(By.cssSelector("button.btn.btn-default")).click();
driver.findElement(By.xpath("//div[#id='content']/div/form")).click();
driver.findElement(By.linkText("Order")).click();
But in the middle of this i have to put sleep's to make it work:
driver.findElement(By.cssSelector("button.btn.btn-default")).click();
Thread.sleep(200);
driver.findElement(By.xpath("//div[#id='content']/div/form")).click();
Thread.sleep(200);
driver.findElement(By.linkText("Order")).click();
Your problems are indicator that you need to construct the test cases better and/or lack of understanding of how the website you are automating works. Especially
stop script for some short period of time with : Thread.sleep(200);
is considered a really bad practice. Whatever you do don't mix implicit and explicit waits, things will start going wrong, explicit waits are the recommended solution.
Waiting for the page to be loaded won't work if (as it seems to be your case) the page is being modified by AJAX operations. Instead of waiting for the page to load, wait for the condition you are testing to become true. This way, you give the AJAX operation time to execute and if your there is a problem you will get an error when the time out occurs.
StaleElementReferenceException is caused by the DOM being refreshed
after you found an element. Remember that a WebElement is a reference
to a specific element on the page, if the DOM get's refreshed this
reference is broken and you need to find the element again to be able to
interact with it.
In my example replace getWebDriver() with your drive instance.
The best practice is to first assert & verify that, that particular
element is present or not. if you see function
assertAndVerifyElement() --- it continuously checks for element for 5
secs and then assert it accordingly.
package com.stackoverflow;
import org.openqa.selenium.By;
import org.testng.Assert;
import org.testng.annotations.Test;
import com.services.Init;
public class Issue3 extends Init {
#Test
public void solutionOfIssue() throws InterruptedException {
/*
* The best thing is to first assert & verify that, that particular
* element is present or not. if you see function
* assertAndVerifyElement() --- it continuously checks for element for 5
* secs and then assert it accordingly.
*/
assertAndVerifyElement(By.cssSelector("button.btn.btn-default"));
getWebDriver().findElement(By.cssSelector("button.btn.btn-default")).click();
assertAndVerifyElement(By.xpath("//div[#id='content']/div/form"));
getWebDriver().findElement(By.xpath("//div[#id='content']/div/form")).click();
assertAndVerifyElement(By.linkText("Order"));
getWebDriver().findElement(By.linkText("Order")).click();
}
public void assertAndVerifyElement(By element) throws InterruptedException {
boolean isPresent = false;
for (int i = 0; i < 5; i++) {
try {
if (getWebDriver().findElement(element) != null) {
isPresent = true;
break;
}
} catch (Exception e) {
// System.out.println(e.getLocalizedMessage());
Thread.sleep(1000);
}
}
Assert.assertTrue(isPresent, "\"" + element + "\" is not present.");
}
}
Hope this will work for you. :)
In order to avoid that, you should locate the element again. After you aquired the element, the reference of its Java object might become stale. Do something like:
WebElement element = WebDriver.findElement(By by) again.
EDIT: For your example try this
driver.findElement(By.cssSelector("button.btn.btn-default")).click();
//30s timeout, use timeout not sleep
WebDriverWait wait = new WebDriverWait(driver, 30);
By xpath = By.xpath("//div[#id='content']/div/form")
//wait for element to be clickable, then click
WebElement element = wait.until(ExpectedConditions.elementToBeClickable(xpath));
element.click();
Error "element is not attached to the page document" display when the element is not exist in current page anymore. It happen because you got element for page 1, and used it in page 1 (or the page was refresh after getting the element, HTML was change by AJAX)

Webdriver, detect DOM changing and wait for it

I am using Webdriver in Java and I encountered an issue repeatedly that I can't find a proper solution yet.
It is to do with doing actions on a page that will cause this page DOM to change (for example, Javascript lightbox), then my JUnit test is expecting new elements after the DOM change but my test is getting the old DOM element.
To give you an example, I have a scenario as below.
First of all click “Add item” button in the below image and the light box appears:
Then fill in all the item details and click "Add & Close". You will see the screen below:
Notice that now there is an info message Your item ... has been added.
Now I put keywords in the Search text box and hit enter and the info message will be changed to below:
In my JUnit test, the flow is like below:
....
itemDetailsPage.clickAddAndClose();
itemDetailsPage.searchItemBy("Electricity");
assertEquals("Your search for 'electricity' returned 2 results.",
itemDetailsPage.getInfoMsg());
....
Now this test is not very robust, because if the network is slow, most of the times, getInfoMsg() will return the previous info message Your item ... has been added instead of the latest info message, which causes the test to fail. Just a side note that these two info message have share the same html element id.
The solution I am trying to implement here are:
add explicit wait in clickAddAndClose()
So it looks something like:
public void clickAddAndClose() {
...
clickWhenReady(driver, By.id(addAndCloseButtonId));
...
waitForElementByLocator(driver,By.id(itemInfoMsgId),10);
}
The second wait proves to be useless because, itemInfoMsgId already exist when the user added the item from the add item lightbox.
add waitForPageLoaded() method at the end of clickAddAndClose() to try to wait for the page to finish reloading. The generic method for waitForPageLoaded() below:
public void waitForPageLoaded(WebDriver driver) {
ExpectedCondition<Boolean> expectation = new ExpectedCondition<Boolean>() {
public Boolean apply(WebDriver driver) {
return ((JavascriptExecutor) driver).executeScript(
"return document.readyState").equals("complete");
}
};
Wait<WebDriver> wait = new WebDriverWait(driver, 30);
try {
wait.until(expectation);
} catch (Throwable error) {
assertFalse("Timeout waiting for Page Load Request to complete.",
true);
}
}
I am expect at the end of clickAddAndClose(), it will see this page is still being updated so it will wait until the info message has been updated. But this does not seem to work either.
That leaves me to the last choice will is to add a thread sleep at the end of clickAddAndClose(). I want to avoid using it.
Is there a generic way of solving this kind of problem? How do I detect that the page DOM is still changing and tell Webdriver to wait until it finishes refreshing?
Waiting for the page to be loaded won't work if (as it seems to be the case) your page is being modified by AJAX operations.
Instead of waiting for the page to load, wait for the condition you are testing to become true. This way, you give the AJAX operation time to execute and if your there is a problem you will get an error when the time out occurs.
I usually use the Python bindings for Selenium and it has been quite a while since I wrote Java code but I believe it would look something like this, with X being replaced with a type appropriate for the itemDetailsPage object:
new FluentWait<X>(itemDetailsPage)
.until(new Function<X, Boolean>() {
public Boolean apply(X itemDetailsPage) {
return "Your search for 'electricity' returned 2 results." == itemDetailsPage.getInfoMsg();
};
});
Seems like you need to wait until ajax has finished its job. In a similar situation I've used a method similar to waitForJQueryProcessing described here. Take a look, it might help.

How do I click on an image button in WebDriver using Java?

The button type is image, and the relevant code in HTML code attached. I have entered all the data and clicked on Apply Now button, it is not at all saving. But when I try to create it manually, it is saved in less than 15 seconds.
Please find the attached screen shot.
The relevant code for the same:
//Navigating to Quick Application
driver.get(QAurl);
Thread.sleep(15000);
driver.findElement(By.id("DdlSalesPerson")).sendKeys("Swamy m Kumara");
driver.findElement(By.id("TxtFName")).sendKeys("Kumar");
driver.findElement(By.id("TxtLName")).sendKeys("Swamy");
driver.findElement(By.id("TxtAddress")).sendKeys("434, Main Road, Somajiguda");
driver.findElement(By.id("TxtZip")).sendKeys("79081");
driver.findElement(By.id("TxtSSN1")).sendKeys("881");
Thread.sleep(15000);
driver.findElement(By.id("TxtSSN2")).sendKeys("72");
driver.findElement(By.id("TxtSSN3")).sendKeys("4365");
Thread.sleep(5000);
driver.findElement(By.id("TxtDayPhone1")).sendKeys("963");
driver.findElement(By.id("TxtDayPhone2")).sendKeys("210");
driver.findElement(By.id("TxtDayPhone3")).sendKeys("5478");
Thread.sleep(5000);
driver.findElement(By.id("ChkIAgree")).click();
driver.findElement(By.id("TxtSignature")).sendKeys("Kumar Swamy");
Thread.sleep(5000);
System.out.println("Entered all the required fields");
//Reading the value in the image.
WebElement element = driver.findElement(By.id(OR.getProperty("FP_SImg_ID")));
String src = ((JavascriptExecutor)driver).executeScript("return arguments[0].attributes['src'].value;", element).toString();
img =src.split("=");
System.out.println("Value retrieved from the Image source: "+img[1]);
driver.findElement(By.id(OR.getProperty("FP_TxtSImg_ID"))).sendKeys(img[1]);
Thread.sleep(5000);
driver.findElement(By.id("TxtEmailId")).sendKeys("abc#abc.com");
driver.findElement(By.name("BtnSubmit")).click();
Thread.sleep(35000);
System.out.println("Successfully Applied from the QuickApp");
HTML code for the Apply now button:
<input id="BtnSubmit" type="image" style="height:33px;width:121px;border-width:0px;"
onclick="javascript:return validateControls();" src="../Common/Images/HybridQA
/apply_now.png" title="Submit Here" tabindex="45" name="BtnSubmit">
Any help will be appreciated.
You have 1 minute 25 seconds of Thread.sleep() in your code...
Remove all the thread.sleep(), if you are waiting for elements to appear do it properly, use an explicit wait:
http://docs.seleniumhq.org/docs/04_webdriver_advanced.jsp
To take an example from the page linked above:
WebDriverWait wait = new WebDriverWait(driver, 10);
WebElement element = wait.until(ExpectedConditions.elementToBeClickable(By.id("someid")));
Have a look at the ExpectedConditions class to see the available conditions built into selenium, if they don't meet your needs it's trivial to write your own expected conditions.
-------------------Edit-------------------
For the record this answer is for the original question that was asked which is quoted below (you can have a look at the edit history of the original question to verify this as well).
Taking long time to save after clicking on Apply Now button using Webdriver in Java
Taking long time to save after clicking on Apply Now button using
Webdriver in Java. I have entered all the data and clicked on Apply
now button, it is not at all saving. But when i try to create it
manually, it is saved in less than 15 seconds. Please find the
attached screen shot.
There could be 2 reasons for this problem.
One is from the HTML code of 'Apply Now' button, I could see that it shows as "input
id='BtnSubmit'", but in your script its written as
'driver.findElement(By.name("BtnSubmit")).click();'.
Shouldn't it be "driver.findElement(By.id("BtnSubmit")).click()"?; 'name' must be
replaced with 'id'.
At the end of the script you click 'BtnSubmit', the session might expire immediately after
you click that button. This problem usually occurs when you use an older and new version
of selenium standalone jar file. See to that you use only the latest version and not in
addition to an old version.
Use this,
driver.findElement(By.id("TxtEmailId")).sendKeys(Keys.ENTER);
after this,
driver.findElement(By.id("TxtEmailId")).sendKeys("abc#abc.com");
and comment,
driver.findElement(By.name("BtnSubmit")).click();
So your code looks like,
driver.findElement(By.id("TxtEmailId")).sendKeys("abc#abc.com");
driver.findElement(By.id("TxtEmailId")).sendKeys(Keys.ENTER);
//driver.findElement(By.name("BtnSubmit")).click();
Sometimes it's difficult to handle image buttons because these buttons are activated when all required fields are entered. Make sure you filled all mandatory fields and press enter after entering last field in the form. First try to do it manually. Instead of clicking on button press enter at last input field and use same stratefy with automation.
Update:
Use your own code and replace Thread.sleep() with below method.
Call it like,
waitForElementToBePresent(By.id("DdlSalesPerson"), 15000);
It waits for next element whichever you pass as argument. It returns true if found or false if not. If element found within the given time it will return true immediately instead of waiting for given time.
public boolean waitForElementToBePresent(By by, int waitInMilliSeconds) throws Exception
{
WebDriver driver = getDriver();
int wait = waitInMilliSeconds;
int iterations = (wait/250);
long startmilliSec = System.currentTimeMillis();
for (int i = 0; i < iterations; i++)
{
if((System.currentTimeMillis()-startmilliSec)>wait)
return false;
List<WebElement> elements = driver.findElements(by);
if (elements != null && elements.size() > 0)
return true;
Thread.sleep(250);
}
return false;
}

selenium web driver test web element has been updated

How would you go about checking to see if a particular WebElement has been updated?
I have a test that performs a form save and then the UI returns a success/error message.
I am doing a series of saves and I need to test and see if the message is what is expected.
Selenium goes so fast that the browser does not have a chance to catch up.
This is the code that I have for testing for an error message (There is an equivalent success message function as well)
public void assertErrorMessage(String errorMsg) {
// set the wait time a bit so the page can load.
driver.manage().timeouts().implicitlyWait(5, TimeUnit.SECONDS);
List<WebElement> results = driver.findElements(By.xpath("//div[#id='lift__noticesContainer__']/descendant::*"));
boolean success = false;
String message = "";
for (WebElement result : results) {
String id = result.getAttribute("id");
if (id.contains("___error")) {
success = true;
}
if (result.getTagName().equals("li")) {
message += result.getText().trim();
}
}
Assert.assertTrue(success, "No error message generated");
Assert.assertTrue(message.equals(errorMsg), "Expecting message: \"" + errorMsg + "\" but got \"" + message + "\"");
}
As this is written, this looks for the presence of a particular <div> and checks to see if contains certain attributes. If it does, get the message.
The thing is, this will always be true and hence my tests are failing since the message is different than the previous one, but the UI hasn't caught up to where selenium is.
My solution so far to force a Thread.sleep(2000) to just pause a bit to allow for the UI to catch up. I know that it is frowned upon to use Thread.sleep().
i.e. (pseudo-code)
page.setField("value");
page.save();
Thread.sleep(2000);
page.assertErrorMessage("Error message");
Is there any way let me check to see if a WebElement has been updated? If so, I could use the Selenium waits to test for that.
Or perhaps someone else has a suggestion for how to do this?
Thanks in advance.
If the result displayed keeps getting every time you perform page.save().
The best way to make sure that your code waits for the browser to update the message is by using the WebDriverWait object.
A simple example would be -
new WebDriverWait(driver, 10).until(ExpectedConditions.textToBePresentInElement(By.xpath("xpath"), "message"));
You can check if that particular element has the message you are looking for. If that message is not present even after 10 seconds then a TimedOutException will be thrown.
You can check out more variations on the ExpectedConditions as suitable in your situation. Hope this helps you.

Categories