I'm using Selenium HtmlUnitDriver and wondering is it possible somehow to keep the state of this driver.
I mean that to test something on the page I have to load the driver -> load and add cookies -> go through the login page -> get required page.
It takes too much time to do it everytime.
Is there something like a 'server state' or maybe I need to serialize and save-load the driver?
Thank you.
Since I've managed to solve my question, I'll leave this here:
1. I took selenium-server-standalone and run it with -browserSessionReuse -timeout 3600 -browserTimeout 600 to keep my session alive.
2. Made my class:
public class MyRemoteWebDriver extends RemoteWebDriver {
....
#Override
protected void startSession(Capabilities desiredCapabilities, Capabilities requiredCapabilities) {
String sid = loadSessionID("SID_NAME");
if (sid != null) {
super.startSession(desiredCapabilities, requiredCapabilities);
log.info("Old SID: " + sid);
setSessionId(sid);
try {
getCurrentUrl();
log.info("Old url: " + getCurrentUrl());
} catch (WebDriverException e) {
sid = null;
}
}
if (sid == null) {
super.startSession(desiredCapabilities, requiredCapabilities);
saveSessionID(getSessionId().toString());
log.info("New SID: " + getSessionId().toString());
}
}
}
So, this way I can store this SessionId in the db and re-use it.
You can try to use a singleton webdriver instead of creating one for each test.
Something like that:
class SingletonWebdriver {
private static Webdriver driver;
public static Webdriver getDriver() {
if(driver == null) {
createDriver();
}
return driver;
}
}
And then you may call getDriver to retrieve the same driver, but in many cases it's a good practice to make each test in a diferent session.
Related
I am trying to check if web page is loaded completed or not (i.e. checking that all the control is loaded) in selenium.
I tried below code:
new WebDriverWait(firefoxDriver, pageLoadTimeout).until(
webDriver -> ((JavascriptExecutor) webDriver).executeScript("return document.readyState").equals("complete"));
but even if page is loading above code does not wait.
I know that I can check for particular element to check if its visible/clickable etc but I am looking for some generic solution
As you mentioned if there is any generic function to check if the page has completely loaded through Selenium the answer is No.
First let us have a look at your code trial which is as follows :
new WebDriverWait(firefoxDriver, pageLoadTimeout).until(webDriver -> ((JavascriptExecutor) webDriver).executeScript("return document.readyState").equals("complete"));
The parameter pageLoadTimeout in the above line of code doesn't really reseambles to actual pageLoadTimeout().
Here you can find a detailed discussion of pageLoadTimeout in Selenium not working
Now as your usecase relates to page being completely loaded you can use the pageLoadStrategy() set to normal [ the supported values being none, eager or normal ] using either through an instance of DesiredCapabilities Class or ChromeOptions Class as follows :
Using DesiredCapabilities Class :
import org.openqa.selenium.WebDriver;
import org.openqa.selenium.firefox.FirefoxDriver;
import org.openqa.selenium.firefox.FirefoxOptions;
import org.openqa.selenium.remote.DesiredCapabilities;
public class myDemo
{
public static void main(String[] args) throws Exception
{
System.setProperty("webdriver.gecko.driver", "C:\\Utility\\BrowserDrivers\\geckodriver.exe");
DesiredCapabilities dcap = new DesiredCapabilities();
dcap.setCapability("pageLoadStrategy", "normal");
FirefoxOptions opt = new FirefoxOptions();
opt.merge(dcap);
WebDriver driver = new FirefoxDriver(opt);
driver.get("https://www.google.com/");
System.out.println(driver.getTitle());
driver.quit();
}
}
Using ChromeOptions Class :
import org.openqa.selenium.WebDriver;
import org.openqa.selenium.firefox.FirefoxDriver;
import org.openqa.selenium.firefox.FirefoxOptions;
import org.openqa.selenium.PageLoadStrategy;
public class myDemo
{
public static void main(String[] args) throws Exception
{
System.setProperty("webdriver.gecko.driver", "C:\\Utility\\BrowserDrivers\\geckodriver.exe");
FirefoxOptions opt = new FirefoxOptions();
opt.setPageLoadStrategy(PageLoadStrategy.NORMAL);
WebDriver driver = new FirefoxDriver(opt);
driver.get("https://www.google.com/");
System.out.println(driver.getTitle());
driver.quit();
}
}
You can find a detailed discussion in Page load strategy for Chrome driver (Updated till Selenium v3.12.0)
Now setting PageLoadStrategy to NORMAL and your code trial both ensures that the Browser Client have (i.e. the Web Browser) have attained 'document.readyState' equal to "complete". Once this condition is fulfilled Selenium performs the next line of code.
You can find a detailed discussion in Selenium IE WebDriver only works while debugging
But the Browser Client attaining 'document.readyState' equal to "complete" still doesn't guarantees that all the JavaScript and Ajax Calls have completed.
To wait for the all the JavaScript and Ajax Calls to complete you can write a function as follows :
public void WaitForAjax2Complete() throws InterruptedException
{
while (true)
{
if ((Boolean) ((JavascriptExecutor)driver).executeScript("return jQuery.active == 0")){
break;
}
Thread.sleep(100);
}
}
You can find a detailed discussion in Wait for ajax request to complete - selenium webdriver
Now, the above two approaches through PageLoadStrategy and "return jQuery.active == 0" looks to be waiting for indefinite events. So for a definite wait you can induce WebDriverWait inconjunction with ExpectedConditions set to titleContains() method which will ensure that the Page Title (i.e. the Web Page) is visible and assume the all the elements are also visible as follows :
driver.get("https://www.google.com/");
new WebDriverWait(driver, 10).until(ExpectedConditions.titleContains("partial_title_of_application_under_test"));
System.out.println(driver.getTitle());
driver.quit();
Now, at times it is possible though the Page Title will match your Application Title still the desired element you want to interact haven't completed loading. So a more granular approach would be to induce WebDriverWait inconjunction with ExpectedConditions set to visibilityOfElementLocated() method which will make your program wait for the desired element to be visible as follows :
driver.get("https://www.google.com/");
WebElement ele = new WebDriverWait(driver, 10).until(ExpectedConditions.visibilityOfElementLocated(By.xpath("xpath_of_the_desired_element")));
System.out.println(ele.getText());
driver.quit();
References
You can find a couple of relevant detailed discussions in:
Selenium IE WebDriver only works while debugging
Selenium how to manage wait for page load?
I use selenium too and I had the same problem, to fix that I just wait also for the jQuery to load.
So if you have the same issue try this also
((Long) ((JavascriptExecutor) browser).executeScript("return jQuery.active") == 0);
You can wrap both function in a method and check until both page and jQuery is loaded
Implement this, Its working for many of us including me. It includes Web Page wait on JavaScript, Angular, JQuery if its there.
If your Application is containing Javascript & JQuery you can write code for only those,
By define it in single method and you can Call it anywhere:
// Wait for jQuery to load
{
ExpectedCondition<Boolean> jQueryLoad = driver -> ((Long) ((JavascriptExecutor) driver).executeScript("return jQuery.active") == 0);
boolean jqueryReady = (Boolean) js.executeScript("return jQuery.active==0");
if (!jqueryReady) {
// System.out.println("JQuery is NOT Ready!");
wait.until(jQueryLoad);
}
wait.until(jQueryLoad);
}
// Wait for ANGULAR to load
{
String angularReadyScript = "return angular.element(document).injector().get('$http').pendingRequests.length === 0";
ExpectedCondition<Boolean> angularLoad = driver -> Boolean.valueOf(((JavascriptExecutor) driver).executeScript(angularReadyScript).toString());
boolean angularReady = Boolean.valueOf(js.executeScript(angularReadyScript).toString());
if (!angularReady) {
// System.out.println("ANGULAR is NOT Ready!");
wait.until(angularLoad);
}
}
// Wait for Javascript to load
{
ExpectedCondition<Boolean> jsLoad = driver -> ((JavascriptExecutor) driver).executeScript("return document.readyState").toString()
.equals("complete");
boolean jsReady = (Boolean) js.executeScript("return document.readyState").toString().equals("complete");
// Wait Javascript until it is Ready!
if (!jsReady) {
// System.out.println("JS in NOT Ready!");
wait.until(jsLoad);
}
}
Click here for Reference Link
Let me know if you stuck anywhere by implementing.
It overcomes the use of Thread or Explicit Wait.
public static void waitForPageToLoad(long timeOutInSeconds) {
ExpectedCondition<Boolean> expectation = new ExpectedCondition<Boolean>() {
public Boolean apply(WebDriver driver) {
return ((JavascriptExecutor) driver).executeScript("return document.readyState").equals("complete");
}
};
try {
System.out.println("Waiting for page to load...");
WebDriverWait wait = new WebDriverWait(Driver.getDriver(), timeOutInSeconds);
wait.until(expectation);
} catch (Throwable error) {
System.out.println(
"Timeout waiting for Page Load Request to complete after " + timeOutInSeconds + " seconds");
}
}
Try this method
This works for me well with dynamically rendered websites:
Wait for complete page to load
WebDriverWait wait = new WebDriverWait(driver, 50);
wait.until((ExpectedCondition<Boolean>) wd -> ((JavascriptExecutor) wd).executeScript("return document.readyState").equals("complete"));
Make another implicit wait with a dummy condition which would always fail
try {
wait.until(ExpectedConditions.visibilityOfElementLocated(By.xpath("//*[contains(text(),'" + "This text will always fail :)" + "')]"))); // condition you are certain won't be true
}
catch (TimeoutException te) {
}
Finally, instead of getting the html source - which would in most of one page applications would give you a different result , pull the outerhtml of the first html tag
String script = "return document.getElementsByTagName(\"html\")[0].outerHTML;";
content = ((JavascriptExecutor) driver).executeScript(script).toString();
There is a easy way to do it. When you first request the state via javascript, it tells you that the page is complete, but after that it enters the state loading. The first complete state was the initial page!
So my proposal is to check for a complete state after a loading state. Check this code in PHP, easily translatable to another language.
$prevStatus = '';
$checkStatus = function ($driver) use (&$prevStatus){
$status = $driver->executeScript("return document.readyState");
if ($prevStatus=='' && $status=='loading'){
//save the previous status and continue waiting
$prevStatus = $status;
return false;
}
if ($prevStatus=='loading' && $status=='complete'){
//loading -> complete, stop waiting, it is finish!
return true;
}
//continue waiting
return false;
};
$this->driver->wait(20, 150)->until($checkStatus);
Checking for a element to be present also works well, but you need to make sure that this element is only present in the destination page.
Something like this should work (please excuse the python in a java answer):
idle = driver.execute_async_script("""
window.requestIdleCallback(() => {
arguments[0](true)
})
""")
This should block until the event loop is idle which means all assets should be loaded.
I am very new to selenium UI automation and I am trying my hands on with a simple application. I am using java with testNG. I need to integrate this test with CI and the test environment url will be different with every deployment. I am passing this as a parameter, difference between test and prod environment is that in the test, there won't be any login screen hence there is no need for authentication but my test verifies login and a login method. I need to be able to skip this test based on the URL supplied. Here is my code and the problem is testNG always suggests that the test was skipped but I can see it executing the login method. Please help me correct or understand what mistake I am committing.
public class Login {
WebDriver driver;
//Parameter - test environment URL
String url = System.getProperty("environment");
#Test (priority = 1)
public void verifyLogin() {
//Skip verifyLogin test in non prod environments
System.setProperty("webdriver.chrome.driver", "C://chromedriver.exe");
WebDriver driver = new ChromeDriver();
driver.manage().window().maximize();
driver.get(url);
if (url=="www.google.com")
//If url matches production url then execute the test, if url doesn't match production URL then skip login test as there won't be any authentication/login in non prod
{
LoginPage login = new LoginPage(driver);
login.signIn();
Assert.assertEquals(driver.getTitle(), "Application Name", "Login failed,");
String title = driver.getTitle();
System.out.println("Page title is " + title);
driver.close();
}
else if (url!="www.google.com"){
throw new SkipException("Skipping this test");
}
}
#Test(priority = 2)
public void userKey() {
System.setProperty("webdriver.chrome.driver", "C://chromedriver.exe");
WebDriver driver = new ChromeDriver();
driver.manage().window().maximize();
driver.get(url);
//If URL equals prod then call the login method to be able to login to the app else just execute the userKey method without having the need to login
if (url=="www.google.com");
{
LoginPage login = new LoginPage(driver);
login.signIn();
}
AccountManagementPage userKey = new AccountManagementPage(driver);
userKey.key();
driver.close();
}
}
The very exact use case is well explained here without throwing SkipException.
The idea is to create a custom annotation for the methods to be skipped & read the methods using IMethodInterceptor - decide to execute or not behind the scenes.
Updated for the question in the comment section:
You do not have to worry about 'TestEnvironment' or 'TestParameters' class.Just directly use the production check logic here.
Predicate<IMethodInstance> isTestExecutingOnProduction = (tip) -> {
return system.getProperty("env").
.equals("<production check here>");
};
So I had it working earlier but I messed up something in my code and now the FluentWait method doesnt seem to call properly. If I run it using quickRun set to false it works as intended (because of the implicit) but when I set it to true it doesnt as it will not wait for the elements to load correctly. Does anyone know exactly what I did wrong?
package myPackage;
import java.util.concurrent.TimeUnit;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import org.openqa.selenium.By;
import org.openqa.selenium.NoSuchElementException;
import org.openqa.selenium.WebDriver;
import org.openqa.selenium.WebElement;
import org.openqa.selenium.safari.SafariDriver;
import org.openqa.selenium.support.ui.FluentWait;
import org.openqa.selenium.support.ui.Wait;
import com.google.common.base.Function;
//import com.gargoylesoftware.htmlunit.javascript.host.Console;
//https://www.codeproject.com/articles/143430/test-your-web-application-s-ui-with-junit-and-sele
//this will open a dynamic page example (ie. youtube) trending
public class youtubeTest {
public boolean quickRun = false; //Disable for debugging otherwise full speed
private static int defaultDebugDelay = 2; //Time in sec for next test to occur in debug
//do no change any of the below
private String testUrl; //target url destination ie youtube
private WebDriver driver; //webdriver instance to reference within class
private int testIndex = 1; //initial index value for console outputting
public WebElement fluentWait(final By locator) {
Wait < WebDriver > wait = new FluentWait < WebDriver > (driver)
.withTimeout(30, TimeUnit.SECONDS)
.pollingEvery(1, TimeUnit.SECONDS)
.ignoring(NoSuchElementException.class);
WebElement foo = wait.until(new Function < WebDriver, WebElement > () {
public WebElement apply(WebDriver driver) {
return driver.findElement(locator);
}
});
return foo;
};
#
Before
public void beforeTest() {
driver = new SafariDriver();
System.out.println("Setting up Test...");
if (quickRun) {
System.out.println("Test Type: Quick Run (Fastest Mode)");
} else {
System.out.println("Test Type: Slow Run (Debug Mode) - Each Test has a " + defaultDebugDelay + " sec call time buffer");
}
testUrl = "https://www.youtube.com";
driver.get(testUrl);
System.out.println("Setting Driver " + driver + "for url: " + testUrl);
}
#
Test
public void Test() {
//insert unit tests within here
//open yt nav menu
locateClickableElement("#appbar-guide-button");
//go to trending
locateClickableElement("#trending-guide-item");
//click on 4th Trending video from list
//locateClickableElement(".expanded-shelf-content-item-wrapper", 3);
locateClickableElement(".expanded-shelf-content-item-wrapper");
}
#
After
public void afterTest() throws Exception {
//wait 10 sec before closing test indefinitely
System.out.println("Test auto ending in 10 seconds...");
Thread.sleep(10000);
stopTest();
}
//individual unit tests
private void locateClickableElement(String ExpectedElement, int child) {
//format string into something like: "ELEMENT:nth-child(1)"
String formattedString = ExpectedElement + ":nth-child(" + child + ")";
System.out.println("Strung: " + formattedString);
locateClickableElement(formattedString);
}
private void locateClickableElement(String ExpectedElement) {
try {
System.out.println("Test " + testIndex + ": locateClickableElement(" + ExpectedElement + ")");
//do absolute delay for visual debugging
if (!quickRun) Thread.sleep(2000);
//click on target if found
fluentWait(By.cssSelector(ExpectedElement)).click();
System.out.println("Test " + testIndex + ": Successful Click on Element(" + ExpectedElement + ")");
} catch (Exception e) {
//whenever error is found output it and end program
System.out.println("Error Could not locateClickableElement(" + ExpectedElement + ")");
System.out.println("Exception Handled:" + e.getMessage());
stopTest("error");
}
testIndex++;
}
private void stopTest() {
System.out.println("Test Completed: Reached End.");
driver.quit();
}
private void stopTest(String typeError) {
System.out.println("Test Completed: With an Error.");
driver.quit();
}
}
I would write this a different way and offer some advice.
Don't slow your test down using "debug mode". If you want to debug your test, use breakpoints and step through the code to see how it's working.
You don't need FluentWait here. A simple WebDriverWait using ExpectedConditions.elementToBeClickable(locator) will work just fine and is less complicated. You shouldn't even need it, if you accept my changes.
Don't pass locators using String, use the intended locator class, By. You won't have to interpret it, translate it, etc. and it will be faster and more flexible.
Unless you are trying to test the UI (which I'm assuming you don't work for youtube), then you can just navigate to the Trending page using the Trending link at the top of the page. It will save you time and clicks. If you aren't testing it, don't test it... get to where you are going as fast as possible. You don't want your test failing due to UI you aren't trying to test and you always want your tests to go as fast as possible. (NOTE: You could even navigate directly to the trending URL.)
You don't need the locateClickableElement() functions. Just click the links... it should be a one liner. If there's an error, it will be obvious. You don't need to print, "There was an error." after an exception message was printed.
You don't need the stopTest() functions... just stop the test. When the browser closes, it will be obvious the test is complete.
The rewritten code is below. It's nice and simple (and short) and should be faster.
public class youtubeTest
{
// do no change any of the below
private String testUrl = "https://www.youtube.com"; // target url destination ie youtube
private WebDriver driver; // webdriver instance to reference within class
private By trendingGuideLinkLocator = By.cssSelector("#trending-guide-item");
private By trendingLinkLocator = By.xpath("//h2[contains(.,'Trending')]");
#Before
public void beforeTest()
{
System.out.println("Setting up Test..."); // if you are going to have this statement, put it at the start of beforeTest()
driver = new SafariDriver();
driver.get(testUrl);
System.out.println("Set Driver " + driver + "for url: " + testUrl);
}
#Test
public void Test()
{
// insert unit tests within here
driver.findElement(trendingLinkLocator).click(); // just click the Trending link, it's faster
driver.findElements(trendingGuideLinkLocator).get(3).click();
}
#After
public void afterTest()
{
driver.close();
driver.quit();
}
}
If you don't want to change all this, the simple answer to your question is replace FluentWait with WebDriverWait.
fluentWait(By.cssSelector(ExpectedElement)).click();
would be replaced by
new WebDriverWait(driver, 10).until(ExpectedConditions.elementToBeClickable(trendingLinkLocator)).click();
Hello i really need help with Selenium WebDriver using TestNG and Excel
i try to get data from excel to open browser and navigate URL. its work successfully and terminal and testng report showing test pass but its not open browser or doing anything its just run its self and show report
Config File
public void openBrowser(String browser){
try {
if (browser.equals("Mozilla")) {
driver = new FirefoxDriver();
} else if(browser.equals("IE")){
driver = new InternetExplorerDriver();
} else if(browser.equals("Chrome")){
System.setProperty("webdriver.chrome.driver", "\\Applications\\Google Chrome.app\\Contents\\MacOS\\Google Chrome ");
driver = new ChromeDriver();
}
} catch (Exception e) {
}
}
public void navigate(String baseUrl){
try {
driver.manage().window().maximize();
driver.manage().deleteAllCookies();
driver.navigate().to(baseUrl);
} catch (Exception e) {
}
}
And Test Execute File
public class NewTest {
public String exPath = Config.filePath;
public String exName = Config.fileName;
public String exWrSheet = "Logiin Functional Test";
public Config config;
#BeforeTest
public void openBrowser() {
config = new Config();
Excel.setExcelFile(exPath+exName, exWrSheet);
String browser = Excel.getCellData(1, 2);
config.openBrowser(browser);
}
#BeforeMethod
public void navigate() {
config = new Config();
Excel.setExcelFile(exPath+exName, exWrSheet);
String baseUrl = Excel.getCellData(2, 2);
config.navigate(baseUrl);
}
#Test
public void test() {
System.out.println("Test");
}
#AfterTest
public void closeBroser() {
//Config.tearDown();
}
I don't have quite enough rep to make a comment, which I would prefer here, but if you aren't getting any sort of exception, I'd suspect the value you're getting for the browser variable is not matching anything in your if-then-else in openBrowser and then falling through. Step through the code with a debugger or just add:
String browser = Excel.getCellData(1, 2);
System.out.println("Browser value from Excel =" + browser);
config.openBrowser(browser);
to see what you're reading from the file.
1 - TestNg is always pass because you are using "void" method and you catch "all" exception
2 - No browser opened because in openBrowser(String browser), NullPointException throws and you already catch it.
-> you need to init an instance of WebDriver and pass it through your test.
I have the following code, this code should pass the test but if i use wrong credentials it still passes the test even though the credentials is incorrect.
I am new to Selenium WebDriver and am not sure what am doing wrong.
public class LoginTest {
String url ="jdbc:sqlserver://CODESV3;databaseName=Codes;integratedSecurity=true";
String DBdriver ="com.microsoft.sqlserver.jdbc.SQLServerDriver";
Connection conn = null;
WebDriver driver = null;
PreparedStatement pstmt=null;
ResultSet rs=null;
#BeforeTest
public void establishConn()
{
driver = new FirefoxDriver();
driver.get("https://10.10.10.50/");
// establish connection
try{
Class.forName(DBdriver).newInstance();
conn = DriverManager.getConnection(url);
}catch(Exception e){
System.out.println("Database failed to connect ");
}
}
#Test
public void testLogin()
{
String forceID="1234";
String username="ayaslem";
String password="Delpiero10+";
boolean valueFound=true;
// Check the db
try{
pstmt=conn.prepareCall("select * from Login where ForceID=?, and Username=? and Password=?");
pstmt.setString(1,forceID);
pstmt.setString(2,username);
pstmt.setString(3,password);
rs=pstmt.executeQuery();
valueFound = rs.next();
}catch(Exception e){
// report some error
}
System.out.println(valueFound);
// login into app
driver.findElement(By.xpath("//*[#id='LogonModel_OrganisationName']")).sendKeys(forceID);
driver.findElement(By.xpath("//*[#id='LogonModel_UserId']")).sendKeys(username);
driver.findElement(By.xpath("//*[#id='LogonModel_Password']")).sendKeys(password);
driver.findElement(By.xpath("//*[#id='maincontent']/form/div/fieldset/div[4]/div[2]/input")).click();
String actual_title = driver.getTitle();
System.out.println(valueFound);
if(valueFound){
Assert.assertEquals( actual_title,"Dashboard");
}else{
Assert.assertEquals(actual_title,"Logon");
}
}
#AfterTest
public void closeConn() throws SQLException
{
if(conn!=null && !conn.isClosed())
{
conn.close();
}
}
}
There are only 2 asserts that might fail your test (apart from any exceptions thrown, of course). So let's just focus on them.
When you provide fake login and password, your valueFound variable is set to false and it follows to the else block, where JUnit asserts whether webdriver is still on the logon site. Since your web application that you're testing most likely stayed on the Logon page after logging in with fake credentials, the assert passes (as it checks whether it's still on logon page, by checking the page's title).
When you provide correct login and password, the if condition passes, since valueFound is set to true after finding such user in the DB and continues on to assert whether Webapp redirected u to the Dashboard page.
I think your test covers both cases well enough, checks if the app stays on current page with fake data, and checks if it navigates forward to dashboard page with real data. It should both pass in my opinion, in both cases.
P.S. Here's a tip - you don't need to use xpath everywhere. There are dedicated methods such as By#id(int) that are there to make your life as an automated test engineer easier :)
Also, using what I call weak xpaths is a really bad practice (//*[#id='maincontent']/form/div/fieldset/div[4]/div[2]/input). Should any ordering on the page change, or any element get wrapped in an intermediary div, your xpath will not locate the element u're looking for. Ask Webapp's developers for ID's or Name's, or use By#tagName("button") on the form container element.