Cannot click element? Execute JavaScript or use the Actions class

Have you ever seen this error in Chrome when trying to click on an element?

org.openqa.selenium.WebDriverException: 
Element is not clickable at point (411, 675)

Clicking the specific element works fine in Firefox but not at all in Chrome.

The code is very straightforward:

By locator = By.xpath("//a[attribute='value']");

WebElement element = driver.findElement(locator);

element.click();

So this does not work sometimes in Chrome.

What do you do?

 

Use an explicit wait

WebDriverWait wait = new WebDriverWait(driver, 25);

By locator = By.xpath("//a[attribute='value']");

WebElement element = wait.until(ExpectedConditions.
                     elementToBeClickable(locator));

element.click();

Unfortunately, this does not work either.

 

 Execute JavaScript

JavascriptExecutor jsExecutor;
jsExecutor = (JavascriptExecutor) driver;

String jsQuery;

jsQuery = "document.querySelector(\"[attribute='value']\").click()"; 

jsExecutor.executeScript(javaScriptCommand);

This code works sometimes only.

So, what do we do next?

 

Use the Actions class to chain methods

WebDriverWait wait = new WebDriverWait(driver, 25);

By locator = By.xpath("//a[attribute='value']");

WebElement element = wait.until(ExpectedConditions.
                                elementToBeClickable(locator));

new Actions(driver()).moveToElement(element)
                     .click()
                     .perform();

WebDriver driver = new FirefoxDriver(); How does it work?

WebDriver driver = new FirefoxDriver();

First line you wrote in test automation.

A line used in all your test automation scripts.

So common and simple.

How does it work?

Why does it need both WebDriver and FirefoxDriver for creating the browser driver object?

 

Lets start.

Instead of talking about theory, we will use a concrete example.

The next test script uses only WebDriver for creating the driver object.

public class TestClass
{
String URL = http://www.vpl.ca;
By SEARCH_TEXT_BOX = By.id("globalQuery");
By SEARCH_BUTTON = By.xpath("//input[@class='search_button']");
String keyword = "java";

@Test
public void testSearch() throws Exception
{
WebDriver webDriver = new WebDriver();
webDriver.get(URL);

WebElement searchTextBox = webDriver.findElement(SEARCH_TEXT_BOX);
searchTextBox.clear();
searchTextBox.sendKeys(keyword);

webDriver.quit();
}
}

The script is very simple:

  1. it creates the driver object
  2. it opens the site url
  3. it finds the search text box element
  4. it clears the text box
  5. it types a value in the text box
  6. it quits the driver object

 

When creating the driver object, only the WebDriver type is used:

WebDriver webDriver = new WebDriver();

And we get an error in Eclipse:

cannot instantiate the type WebDriver

Logically, this makes sense.

Which browser driver are we talking about?

Is it the driver for Firefox, Chrome, Internet Explorer?

 

So, we can replace the WebDriver type with another type, for example, FirefoxDriver.

public class TestClass {

String URL = http://www.vpl.ca;
By SEARCH_TEXT_BOX = By.id("globalQuery");
By SEARCH_BUTTON = By.xpath("//input[@class='search_button']");
String keyword = "java";

@Test
public void testSearch() throws Exception
{
FirefoxDriver firefoxDriver = new FirefoxDriver();
firefoxDriver.get(URL);

WebElement searchTextBox = firefoxDriver.findElement(SEARCH_TEXT_BOX);
searchTextBox.clear();
searchTextBox.sendKeys(keyword);

firefoxDriver.quit();
}
}

No error is generated in this case and the code can be executed.

Since the driver object is both created and quitted in the test script, we can move these things in the test fixtures (setUp() and tearDown() methods annotated with @Before and @After):

public class TestClass {

String URL = http://www.vpl.ca;
By SEARCH_TEXT_BOX = By.id("globalQuery");
By SEARCH_BUTTON = By.xpath("//input[@class='search_button']");
String keyword = "java";
FirefoxDriver firefoxDriver;

@Before
public void setUp()
{
firefoxDriver = new FirefoxDriver();
}

@After
public void tearDown()
{
firefoxDriver.quit();
}

@Test
public void testSearch() throws Exception
{
firefoxDriver.get(URL);

WebElement searchTextBox = firefoxDriver.findElement(SEARCH_TEXT_BOX);
searchTextBox.clear();
searchTextBox.sendKeys(keyword);

}

We could even create a separate method that returns the driver object:

public class TestClass{

String URL = http://www.vpl.ca;
By SEARCH_TEXT_BOX = By.id("globalQuery");
By SEARCH_BUTTON = By.xpath("//input[@class='search_button']");
String keyword = "java";
FirefoxDriver firefoxDriver;

@Before
public void setUp()
{
firefoxDriver = getFirefoxDriver();
}

@After
public void tearDown()
{
firefoxDriver.quit();
}

@Test
public void testSearch() throws Exception
{
firefoxDriver.get(URL);

WebElement searchTextBox = firefoxDriver.findElement(SEARCH_TEXT_BOX);
searchTextBox.clear();
searchTextBox.sendKeys(keyword);

}

private FirefoxDriver getFirefoxDriver()
{
return new FirefoxDriver();
}

}

Things work pretty well in the present script.

But lets say that we want to execute the script in Chrome.

We will need to make a few changes to the test class:

  • the FirefoxDriver field will use the ChromeDriver type
  • a new getChromeDriver() method is needed
  • the getChromeDriver) method returns a ChromeDriver object

 

public class TestClass {

String URL = http://www.vpl.ca;
By SEARCH_TEXT_BOX = By.id("globalQuery");
By SEARCH_BUTTON = By.xpath("//input[@class='search_button']");
String keyword = "java";
ChromeDriver chromeDriver;

@Before
public void setUp()
{
chromeDriver = getChromeDriver();
}

@After
public void tearDown()
{
chromeDriver.quit();
}

@Test
public void testSearch() throws Exception
{
chromeDriver.get(URL);

WebElement searchTextBox = chromeDriver.findElement(SEARCH_TEXT_BOX);
searchTextBox.clear();
searchTextBox.sendKeys(keyword);

}

private ChromeDriver getChromeDriver()
{
System.setProperty("webdriver.chrome.driver", "C:\\Selenium\\chromedriver.exe");
return new ChromeDriver();
}

}

This script works as well but things are getting complicated.

How can we modify the script to work for both browsers instead of having a script for each browser?

We can combine the getFirefoxBrowser() and getChromeBrowser() methods into a single method.

private ????? getDriver(String browserName) throws Exception
{
if (browserName.equalsIgnoreCase("chrome"))
return getChromeDriver();

if (browserName.equalsIgnoreCase("firefox"))
return getFirefoxDriver();

throw new Exception("invalid browser name");
}

The method has a parameter for the browserName and returns either a ChromeDriver or a FirefoxDriver object.

Obviously, this is a problem as we cannot have 2 different types as the method return type.

The method cannot return FirefoxDriver some times and ChromeDriver other times.

To solve this, we need a type that can be used instead of both FirefoxDriver and ChromeDriver.

 

And, you guesses, this is WebDriver.

Why can WebDriver be used instead of FirefoxDriver and ChromeDriver?

Because, in comparison with FirefoxDriver() and ChromeDriver() which are classes (so objects can be created for them), WebDriver is an interface.

 

An interface is just a template that is implemented by a class.

It specifies what fields and methods the class should have but without providing more details.

The classes that implement the interface will provide the methods implementations.

So WebDriver is an interface and FirefoxDriver and ChromeDriver classes that implement the WebDriver interface.

See below a part of the WebDriver interface (from selenium-java-2.52.0-srcs.jar file):

 
public interface WebDriver extends SearchContext {

void get(String url);
String getCurrentUrl();
String getTitle();
List<WebElement> findElements(By by);
WebElement findElement(By by);
String getPageSource();
void close();
void quit();

.........................................

.........................................

}

Just method declarations and no method implementations.

What about FirefoxDriver()?

FirefoxDriver() is not really implementing the WebDriver interface.

It is inheriting instead from the RemoteDriver class which implements the WebDriver interface.

This is some content of the RemoteDriver class:

public class RemoteWebDriver implements WebDriver, JavascriptExecutor,FindsById, FindsByClassName, FindsByLinkText, FindsByName, FindsByCssSelector, FindsByTagName, FindsByXPath,
HasInputDevices, HasCapabilities, TakesScreenshot {

private static final Logger logger = Logger.getLogger(RemoteWebDriver.class.getName());
private Level level = Level.FINE;

private ErrorHandler errorHandler = new ErrorHandler();
private CommandExecutor executor;
private Capabilities capabilities;
private SessionId sessionId;
private FileDetector fileDetector = new UselessFileDetector();
private ExecuteMethod executeMethod;

public void get(String url) {
execute(DriverCommand.GET, ImmutableMap.of("url", url));
}

public String getTitle() {
Response response = execute(DriverCommand.GET_TITLE);
Object value = response.getValue();
return value == null ? "" : value.toString();
}

public String getCurrentUrl() {
Response response = execute(DriverCommand.GET_CURRENT_URL);
if (response == null || response.getValue() == null) {
throw new WebDriverException("Remote browser did not respond to getCurrentUrl");
} else {
return response.getValue().toString();
}
}

public List<WebElement> findElements(By by) {
return by.findElements(this);
}

public WebElement findElement(By by) {
return by.findElement(this);
}

protected WebElement findElement(String by, String using) {
if (using == null) {
throw new IllegalArgumentException("Cannot find elements when the selector is null.");
}

Response response = execute(DriverCommand.FIND_ELEMENT,
ImmutableMap.of("using", by, "value", using));
Object value = response.getValue();
WebElement element;
try {
element = (WebElement) value;
} catch (ClassCastException ex) {
throw new WebDriverException("Returned value cannot be converted to WebElement: " + value, ex);
}
setFoundBy(this, element, by, using);
return element;
}

Lots of method implementations in this case.

That is why, in the first code example, we could not create the WebDriver object. Because WebDriver is an interface and not a class.

It is not possible to create an object for an interface and instantiate it using the interface type.

Interfaces have a very interesting characteristic, though.

It is possible to create an object for an interface and instantiate it using any of the classes that implements the interface.

For example,

WebDriver driver = new FirefoxDriver();

or

WebDriver driver = new ChromeDriver();

Looks familiar?

Having clarified the difference between WebDriver, FirefoxDriver and ChromeDriver, we can finalize the sample project code:

public class TestClass {

String URL = http://www.vpl.ca;
By SEARCH_TEXT_BOX = By.id("globalQuery");
By SEARCH_BUTTON = By.xpath("//input[@class='search_button']");
String keyword = "java";
WebDriver driver;

@Before
public void setUp() throws Exception
{
driver = getDriver("firefox"); 
//or driver = getDriver("chrome");
}

@After
public void tearDown()
{
driver.quit();
}

@Test
public void testSearch() throws Exception
{
driver.get(URL);

WebElement searchTextBox = driver.findElement(SEARCH_TEXT_BOX);
searchTextBox.clear();
searchTextBox.sendKeys(keyword);
}

private ChromeDriver getChromeDriver()
{
String chromeDriverPath = 
"C:\\Selenium\\BrowserDrivers\\chromedriver.exe";
System.setProperty("webdriver.chrome.driver", chromeDriverPath);
return new ChromeDriver();
}

private FirefoxDriver getFirefoxDriver()
{
return new FirefoxDriver();
}

private WebDriver getDriver(String browserName) throws Exception
{
if (browserName.equalsIgnoreCase("chrome"))
return getChromeDriver();

if (browserName.equalsIgnoreCase("firefox"))
return getFirefoxDriver();

throw new Exception("invalid browser name");
}

}

The driver class field uses the WebDriver interface for its type.

The getDriver() method returns a WebDriver object that is initialized with either a FirefoxDriver object or with a ChromeDriver object.

Thats why all these types are needed.

So that your scripts are flexible and can use any WebDriver object is required.

 

 

Start the Chrome Browser with Extensions

You may need sometimes to run test automation scripts in the Chrome browser using Chrome extensions.

By default, when any browser is started by Selenium, no extensions are added to it.

Also, all browser options are the default ones.

There are 2 ways of starting Chrome with extensions.

 

1. Use a Custom Chrome Profile

The following steps can be used for starting Chrome with a custom profile:

  1. Create a folder for the new custom browser profile (example: C:\Selenium\BrowserProfile)
  2. In the Chrome bar, run the following command: chrome://version/
  3. Copy the Chrome executable folder path value

 

chrome version

4. Open Command Prompt

5. Go to the Chrome executable folder path using the following 2 commands:

cd /

cd C:\Program Files (x86)\Google\Chrome\Application

5. Execute the following command:

chrome -user-data-dir=C:\\Selenium\\BrowserProfile

6. The Chrome browser is opened and a new profile is created in the C:\Selenium\BrowserProfile folder.

7. Add a new extension to Chrome by going to

Settings–> Extensions –> Get More Extensions

8. Close the browser

9. Add the needed settings to the Selenium code:

 

public class TestClass {

WebDriver driver;

 

@Before
public void setUp() {

System.setProperty(“webdriver.chrome.driver”, “C:\\Selenium\\BrowserDrivers\\chromedriver.exe”);

ChromeOptions options = new ChromeOptions();

options.addArguments(“user-data-dir=C:\\Selenium\\BrowserProfile”);

options.addArguments(“–start-maximized”);

driver = new ChromeDriver(options);

}

 
@After
public void tearDown() {

driver.quit();

}

 

@Test
public void testScript() {

Thread.sleep(10000);

}
}

 

10. Run the code; the Chrome browser is opened and the new extension is added to it

These steps are also useful if other custom settings have to be set for the browser.

 

chrome extension

 

2. Load a Chrome Extension

The following steps can be used for loading Chrome with an extension:

  1. Run the following url in Chrome: chrome://extensions/
  2. Enable the developer mode
  3. Copy the id of the extension

developer mode

 

4. Run the following url in Chrome: chrome://version/

5. Copy the Chrome profile path

chrome profile path

6. Open the Chrome profile path in Windows Explorer

7. Open the Extensions folder

8. Open the folder that has the name equal to the extension id from step 3

9. If there is a version subfolder in the extension folder, open it

10. Copy the folder path

extension folder

11. Add the needed settings to the code:

public class TestClass {

WebDriver driver;

 

@Before
public void setUp() {

String pathToExtension = “C:\\Users\\home\\AppData\\Local\\Google\\Chrome\\User Data\\Default\\Extensions\\mbopgmdnpcbohhpnfglgohlbhfongabi\\2.3.1_0”;

ChromeOptions options = new ChromeOptions();

options.addArguments(“–load-extension=” + pathToExtension);

driver = new ChromeDriver(options);

}

 

@After
public void tearDown() {

driver.quit();

}

 

@Test
public void testScript() {

Thread.sleep(10000);

}
}

12. Run the code; the Chrome browser is opened and the new extension is added to it

 

chrome extension

 

How to Take Screenshots if The Selenium Script Fails

Taking a screenshot when your Selenium WebDriver script fails is a very useful technique.

It shows you the page where failure has happened.

The best way of taking the screenshot is through JUNIT rules.

 

 

bugs bunny upset

 

Incorrect Way of Taking a Screenshot

A short example will be used for our “take screenshot” discussion.

The example automates a very simple test case that

  1. opens the http://www.vpl.ca site
  2. executes a keyword search
  3. verifies that the number of results is greater than 0

 

The example uses the following classes:

  • TEST CLASS
    • the test class extends a test base class
    • it has 1 test script only
    • the test script uses page objects and assertions

 

  • TEST BASE CLASS
    • it is used for setting up and cleaning the test environment

 

  • HOME PAGE CLASS
    • it implements the user actions related to the home page (open page, search)

 

  • RESULTS PAGE CLASS
    • it implements the user actions related to results page (get results count)

 

  • SCREENSHOT CLASS
    • it takes a screenshot of the current page

 

TEST CLASS

public class HomePageTests extends TestBase{

String keyword = “java”;

 

@Test
public void searchReturnsResults() throws Exception {

HomePage homePage = new HomePage();

homePage.openIn(browser);

ResultsPage resultsPage = homePage.search(keyword);

assertTrue(resultsPage.getResultCount() > 0);

}

}

 

TEST BASE CLASS

public class TestBase {

public static WebDriver browser;

 

@BeforeClass
public static void setUp() {
browser = new Driver().get();
}

 

@AfterClass
public static void tearDown() {
browser.quit();

}

}

 

HOME PAGE CLASS

public class HomePage {

By searchTextBoxId = By.id(“globalQuery”);
By searchButtonLocator = By.xpath(“//input[@class=’search_button1′]”);

String url = “http://www.vpl.ca&#8221;;

WebDriver browserDriver;

 

public void openIn(WebDriver driver) throws Exception
{
try
{
browserDriver = driver;
browserDriver.get(url);
}
catch (Exception ex)
{
new Screenshot(browserDriver).capture(“HomePage class – openIn method”);

throw new Exception(“site cannot be loaded”);
}

}

 

public ResultsPage search(String keyword) throws Exception {

try
{
WebElement searchTextBox = browserDriver.findElement(searchTextBoxId);
searchTextBox.sendKeys(keyword);

WebElement searchButton = browserDriver.findElement(searchButtonLocator);
searchButton.click();

return new ResultsPage(browserDriver);
}
catch (Exception ex)
{
new Screenshot(browserDriver).capture(“HomePage class – search method”);

throw new Exception(“search method failed”);
}

}

}

 

RESULTS PAGE CLASS

public class ResultsPage {

By itemCountLocator = By.xpath(“//span[@class=’items_showing_count’]”);

WebDriver browserDriver;

 

public ResultsPage(WebDriver driver)
{
browserDriver = driver;
}

 

public int getResultCount() throws Exception {

int count = 0;

try
{

WebElement resultsCount = browserDriver.findElement(itemCountLocator);

String value = resultsCount.getText();

int index1 = value.indexOf(“of”) + 3;
int index2 = value.indexOf(“items”) – 1;

count = Integer.parseInt(value.substring(index1, index2));
}
catch (Exception ex)
{
new Screenshot(browserDriver).capture(“ResultsPage class – getResultCount method”);

throw new Exception(“result count cannot be determined”);
}

return count;

}
}

SCREENSHOT CLASS

public class Screenshot {

WebDriver driver;

 

public Screenshot(WebDriver driver)
{
this.driver = driver;
}

 

public void capture(String name) {

try {

new File(“src/screenshots”).mkdirs(); 
FileOutputStream out = new FileOutputStream
(“src/screenshots/” + name + “.png”);
out.write(((TakesScreenshot) driver).getScreenshotAs(OutputType.BYTES));

out.close();

}
catch (Exception e) {
}

}

}

 

The Screenshot class is responsible for taking a screenshot of the current page of the site and storing it in the /src/screenshots folder.

Each of the page object classes (HomePage, ResultsPage) uses a try/catch block for catching exceptions in each of their methods.

If an exception is caught, a screenshot of the current site page is generated in the catch() block.

 

This approach works well but it leads to a lot of duplicated code that needs to be maintained.

Each method will need to have code for taking the screenshot in the catch() block.

The more classes you have, the more of the duplicated code.

JUNIT rules offer a great way of simplifying the process of taking a screenshot in case of failures.

 

 

 

bugs-bunny

Use JUNIT Rules for Automatic Screenshot Taking

 

JUNIT  Rules allow very flexible addition or redefinition of the behavior of each test method in a test class.

Testers can reuse or extend one of the default Rules or write their own.

The rules are useful in situations when you want to build your own test automation report, when you want to take automatic screenshots, etc.

Read more about JUNIT rules here.

A very good tutorial on JUNIT rules can be found here.

 

How will JUNIT rules help with our code?

 

First,

We create a custom JUNIT rule that takes a screenshot automatically every time an exception is generated in the project:

 

public class ScreenshotTestRule implements TestRule {

WebDriver driver;

 

public ScreenshotTestRule (WebDriver driver)
{
this.driver = driver;
}

 
public Statement apply(final Statement statement, final Description description) {

return new Statement() {

@Override
public void evaluate() throws Throwable {
try {
statement.evaluate();
}

catch (Throwable t) {
new Screenshot(driver).capture(getName(description.getClassName(), description.getMethodName()));

throw t;
}
}

 

private String getName(String className, String methodName) {
return className.substring(className.lastIndexOf(“.”) + 1) + “_” + methodName;
}
};
}
}

 

Second,

We add the JUNIT rule to the test base class:

 

public class TestBase {

public static WebDriver browser;

 

@Rule
public ScreenshotTestRule screenshotTestRule = new ScreenshotTestRule(browser);

 

@BeforeClass
public static void setUp() {
browser = new Driver().get();
}

 

@AfterClass
public static void tearDown() {
browser.quit();

}

}

 

Third,

We remove the “screenshot” code from the page object classes:

public class HomePage {

By searchTextBoxId = By.id(“globalQuery”);
By searchButtonLocator = By.xpath(“//input[@class=’search_button1′]”);

String url = “http://www.vpl.ca&#8221;;

WebDriver browserDriver;

 

public void openIn(WebDriver driver) throws Exception
{
try
{
browserDriver = driver;
browserDriver.get(url);
}
catch (Exception ex)
{
throw new Exception(“site cannot be loaded”);
}

}

 

public ResultsPage search(String keyword) throws Exception {

try
{
WebElement searchTextBox = browserDriver.findElement(searchTextBoxId);
searchTextBox.sendKeys(keyword);

WebElement searchButton = browserDriver.findElement(searchButtonLocator);
searchButton.click();

return new ResultsPage(browserDriver);
}
catch (Exception ex)
{
throw new Exception(“search method failed”);
}

}

}

 

How does this work?

Every time an exception happens, the JUNIT custom rule is triggered.

The JUNIT rule generates a screenshot of the current page (the one where the exception occurs).

The screenshot is then saved in the /src/screenshot folder.

 

I hope that this article makes it clear how to take screenshots in your automation projects.

If you liked it, please share it with your friends.

 

 

bugs-bunny-thats-all-folks

 

Do not use WebDriver APIs in the Test Script

Test automation starts with a test case.

For example:

1. open the home page of the site (http://www.vpl.ca)

2. run a keyword search

3. validate that the number of results for the keyword search is greater than 0

 

The test case is high level and includes actions that a user would take.

 

To automate it, it needs to be described at a lower level:

1. open the home page of the site (http://www.vpl.ca)

2. run a keyword search

2.1 find the search textbox element
2.2 type a keyword in the search textbox element
2.3 find the search button
2.4 click the search button

3. validate that the number of results for the keyword search is greater than 0

3.1 find the result count label element
3.2 get the value of the result count label
3.3 extract the number of results from the result count value
3.4 verify that the number of results is greater than 0

The code of the test automation script is below:

@Test
public void testFirstResult() {

String url = “http://www.vpl.ca&#8221;;

By searchTextBoxLocator = By.xpath(“//input[@id=’globalQuery’]”);

By searchButtonLocator = By.xpath(“//input[@class=’search_button’]”);

By resultsCountLocator = By.xpath(“//span[@class=’items_showing_count’]”);

 

driver.get(url);

WebElement searchField = driver.findElement(searchTextBoxLocator );

searchField.click();

searchField.sendKeys(“java”);

WebElement searchButton = driver.findElement(searchButtonLocator );

searchButton.click();

WebElement resultCountLabel = driver.findElement(resultsCountLocator );

String resultCountValue = resultCountLabel.getText();

int index1 = resultCountValue.indexOf(“of”) + 3;
int index2 = resultCountValue.indexOf(“items”) – 1;

resultCountValue = resultCountValue.substring(index1, index2);

int resultCount = Integer.parseInt(resultCountValue);

assertTrue(resultCount > 0);

}

 

The script includes many WebDriver API objects and methods:

  • WebElement objects
  • By objects
  • driver.findElement() method
  • getText() method
  • click(), sendkeys() methods()

 

Manual testers learning test automation write code that looks like this.

The code works.

Imagine having 50 other scripts similar to this.

What’s wrong here?

Scripts like this one have multiple problems:

– they are difficult to understand by manual testers with no programming experience
– they are difficult to change
– they are difficult to maintain

This way of writing test automation code is incorrect as per Simon Stewart, creator of Selenium WebDriver:

If you have WebDriver APIs in your test methods, You’re Doing It Wrong.

Simon Stewart.

 

So how do we fix the test scripts?

The Page Object Model helps:

“A page object wraps an HTML page, or fragment, with an application-specific API, allowing you to manipulate page elements without digging around in the HTML.”

Martin Fowler

 

This model states that page object classes should be created for each web page of the site.

The page object classes implement the interaction with the site using Selenium WebDriver APIs.

The test automation scripts use then the page object classes and assertions.

Basically, what we should do is to structure the test script so that it follows not the detailed test case but the high level one:

 

@Test
public void testResultsInfo()
{

HomePage home = new HomePage(driver);

home.open();

ResultsPage results = home.search();

assertTrue(results.resultsCount() > 0);

}

 

The test script does not include WebDriver API any longer.

It is as easy to read as the high level test case.

It uses 2 page object classes:

  1. HomePage
  2. ResultsPage

Lets see how the code can be added to these 2 classes.

 

HomePage class

It implements all user actions for the home page.

In this case, there are 2 actions only:

  1. open() – open the home page of the site
  2. search() – run a keyword search

 

public class HomePage
{

WebDriver driver;

String url = “http://www.vpl.ca&#8221;;

By searchTextBoxLocator = By.xpath(“//input[@id=’globalQuery’]”);

By searchButtonLocator = By.xpath(“//input[@class=’search_button’]”);

public HomePage(WebDriver driver)
{

this.driver = driver;

}
public void open()
{

driver.get(url);

}

public ResultsPage search(String keyword)
{

WebElement searchField = driver.findElement(searchTextBoxLocator);

searchField.click();

searchField.sendKeys(keyword);

WebElement searchButton = driver.findElement(searchButtonLocator);

searchButton.click();

return new ResultsPage(this.driver);

}

 

 

ResultsPage class

For the results page, we just need to have a method that returns the number of results:

public class ResultsPage {

WebDriver driver;

By resultsCountLocator = By.xpath(“//span[@class=’items_showing_count’]”);

public ResultsPage(WebDriver driver)
{

this.driver = driver;

}

public integer resultsCount()
{

WebElement resultCountLabel = driver.findElement(resultsCountLocator);

String resultCountValue = resultCountLabel.getText();

int index1 = resultCountValue.indexOf(“of”) + 3;
int index2 = resultCountValue.indexOf(“items”) – 1;

resultCountValue = resultCountValue.substring(index1, index2);

int resultCount = Integer.parseInt(resultCountValue);

return returnCount;

}

 

To summarize, the test automation scripts should not include WebDriver APIs.

Page object classes should be created for implementing user actions for each page.

The test scripts should use only page objects and assertions.

The Beginner’s Guide To Explicit Waits

explicit wait flow

Test automation methods should synchronize with the web site every time they interact with elements.

For example, before clicking an element, the test method should make sure first that the element exists and that it is clickable.

Otherwise, an exception will be generated.

The synchronization is done using explicit waits and expected conditions.

Why do we need explicit waits?

Lets take the simplest Selenium WebDriver method:

driver.findElement(locator)

How does it work?

findElement() tries finding in the browser DOM the element matched by the locator.

If the element is found, findElement() returns it.

Otherwise, findElement() fails.

findElement() works well if the website is fast.

But if the website is slow and the element is not in the browser DOM when findElement() is executed, findElement() will fail.

We need a better way of interacting with website elements.

What are Explicit Waits?

Explicit wait objects are created for the WebDriverWait class.

Each wait object has 2 parameters:

  • the driver object
  • a timeout

First, we create the object:

WebDriverWait wait = new WebDriverWait(driver, timeout);

Then, we tell the wait object to wait until an expected condition is reached:

  • wait until the element is found and clickable
  • wait until the page title is correct
  • wait until the element is found and visible
  • wait until the page url matches a pattern

The waiting is being done by the until() method of the WebDriverWait class.

The until() method has a parameter as well which is the expected condition to wait for.

The expected condition is created using the ExpectedConditions class:

wait.until(ExpectedConditions.condition(parameter));

To explain how an explicit wait works, I will use the following example:

//create the id locator for the searchBox element
By searchBoxId = By.id("search-box");

//create the wait object
WebDriverWait wait = new WebDriverWait(driver, 10);

//find the searchBox element and save it in the WebElement variable
WebElement searchBoxElement = wait.until(
                                ExpectedConditions.
                                  elementToBeClickable
                                     (searchBoxId));

//type in the searchBox element
searchBoxElement.click();
searchBoxElement.clear();
searchBoxElement.sendKeys("java");

What does this code work?

  1. the locator of the searchBox element is created
  2. the wait object is created with the driver object and a 10 seconds timeout as parameters.
  3. the waiting for the searchBox element starts:
    1. until() method starts a timer.
    2. until() method verifies if searchBox is in the browser dom and is clickable
    3. if the condition is true (searchBox is in the browser dom and is clickable), the waiting ends and until() method returns the searchBox element
    4. if the condition is not met and the timer did not reach yet the timeout value, the code waits for 500 ms before continuing from step 3.2
  4. if until() method finds the element, it returns the searchBox element which is saved in the WebElement variable
  5. the code clicks the element, clears existing value and types the keyword in it
  6. if until() method cannot find the searchBox element within the 10 seconds timeout, it generates an exception; the remaining code is not executed

 

See below a simplified diagram that explains this process:

 

explicit wait

What can we do with explicit waits?

Explicit waits can be used for:

1. finding single web element

2. finding multiple web elements

3. checking the web page title and url

4. checking the element’s status

5. interacting with frames (not included in this article)

 

Find single web element

 

There are a few expected conditions that can help with finding a single web element.

1. elementToBeClickable

It defines an expectation for checking that an element is visible and enabled so that you can click it.

ExpectedCondition elementToBeClickable(By locator)
ExpectedCondition elementToBeClickable(WebElement element)

2. presenceOfElementLocated

It defines an expectation for checking that an element is present on the DOM of a page.

The element can be enabled or disabled.

Use this condition for checking for hidden elements.

ExpectedCondition presenceOfElementLocated(By locator)

3. visibilityOfElementLocated

ExpectedCondition visibilityOfElementLocated(By locator)

ExpectedCondition visibilityOf(WebElement element)

It defines an expectation for checking that an element is present on the DOM of a page and visible.

 

NOTE: All following examples are complete so that you can try them by yourself.

Example

The next code sample shows how the elementToBeClickable and visibilityOfElementLocated conditions are used for finding single elements.

It uses the Vancouver Public Library website.

It automates a test case that verifies if the search component works.

import org.junit.After;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import org.openqa.selenium.By;
import org.openqa.selenium.WebDriver;
import org.openqa.selenium.WebElement;
import org.openqa.selenium.chrome.ChromeDriver;
import org.openqa.selenium.support.ui.ExpectedConditions;
import org.openqa.selenium.support.ui.WebDriverWait;

public class TestClass { 
  WebDriver driver; 
  WebDriverWait wait; 

  //this is where chromedriver.exe should be 
  String driverPath = "c:/browserdrivers/chromedriver.exe"; 

  @Before 
  public void setUp() { 
    System.setProperty("webdriver.chrome.driver"driverPath);           
    driver = new ChromeDriver(); 
    wait = new WebDriverWait(driver, 10); 
  } 

  @After 
  public void tearDown() { 
    driver.quit(); 
  } 
 
  @Test 
  public void clickElementWithExplicitWait() 
      throws InterruptedException { 
  
    //open site 
    driver.get("http://www.vpl.ca"); 

    //finds the search box using the elementToBeClickable condition
    By searchBoxId = By.id("edit-search"); 
    WebElement searchBox = wait.until(
                             ExpectedConditions
                               .elementToBeClickable
                                  (searchBoxId)); 

    //types in the search box 
    searchBox.click(); 
    searchBox.clear(); 
    searchBox.sendKeys("java"); 
    
    //finds search button using the visibilityOfElementLocated condition
    By searchButtonId = By.id("edit-submit"); 
    WebElement searchButton = wait.until(
                                ExpectedConditions.
                                  visibilityOfElementLocated
                                      (searchButtonId)); 
    
    //clicks the search button 
    searchButton.click();

    //delay so you can see whats happening in the browser 
    Thread.sleep(5000); 
  }
} 

 

find multiple web elements

There are a few expected conditions that can help with finding multiple web elements.

1. visibilityOfAllElementsLocatedBy

It defines an expectation for checking that all elements present on the web page that match the locator are visible.

ExpectedCondition<List> visibilityOfAllElementsLocatedBy(By locator)
ExpectedCondition<List> visibilityOfAllElements(List elements)

 

2. presenceOfAllElementsLocatedBy

It defines an expectation for checking that there is at least 1 element present on the page.

ExpectedCondition<List> presenceOfAllElementsLocatedBy(By locator)

Example

The next code sample continues the previous one.

After the search is done, on the results page, it checks that the

  • title count is 25
  • author count is > 0

It finds the title and author elements using the 2 expected conditions for finding multiple elements:

import static org.junit.Assert.*;
import static org.junit.Assert.*;
import java.util.List;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import org.openqa.selenium.By;
import org.openqa.selenium.WebDriver;
import org.openqa.selenium.WebElement;
import org.openqa.selenium.chrome.ChromeDriver;
import org.openqa.selenium.support.ui.ExpectedConditions;
import org.openqa.selenium.support.ui.WebDriverWait;

public class TestClass { 
  WebDriver driver; 
  WebDriverWait wait; 

  //this is where chromedriver.exe should be 
  String driverPath = "c:/browserdrivers/chromedriver.exe"; 

  @Before 
  public void setUp() { 
    System.setProperty("webdriver.chrome.driver"driverPath); 
    driver = new ChromeDriver(); 
    wait = new WebDriverWait(driver, 10); 
  } 

  @After 
  public void tearDown() { 
    driver.quit(); 
  } 

  @Test 
  public void clickElementWithExplicitWait() 
    throws InterruptedException { 

    //open site 
    driver.get("http://www.vpl.ca"); 

    //finds the search box 
    By searchBoxId = By.id("edit-search"); 
    WebElement searchBox = wait.until
                              (ExpectedConditions
                                  .elementToBeClickable
                                     (searchBoxId)); 

    //types in the search box 
    searchBox.click(); 
    searchBox.clear(); 
    searchBox.sendKeys("java"); 

    //finds the search button 
    By searchButtonId = By.id("edit-submit"); 
    WebElement searchButton = wait.until
                               (ExpectedConditions
                                 .visibilityOfElementLocated
                                   (searchButtonId)); 

    //clicks the search button 
    searchButton.click(); 

    //delay so you can see whats happening in the browser 
    Thread.sleep(10000); 

    //find all titles using visibilityOfAllElementsLocatedBy 
    By titleLocator = By.xpath("//a[@testid = 'bib_link']"); 
    List<WebElement> titles = wait.until
                               (ExpectedConditions.
                                  visibilityOfAllElementsLocatedBy
                                    (titleLocator)); 

    //get the number of titles found 
    int titleCount = titles.size();

    //check that the title count is equal to 25 
    assertEquals(titleCount, 25); 
    System.out.println("title count = " + titleCount);

    //find all authors using presenceOfAllElementsLocatedBy 
    By authorLocator = By.xpath("//a[@testid = 'author_search']"); 
    List<WebElement> authors = wait.until(
                                 ExpectedConditions
                                   .presenceOfAllElementsLocatedBy
                                     (authorLocator)); 

    //get the number of authors found 
    int authorCount = authors.size();
  
    //check that the author count is > 0 
    assertTrue(authorCount > 0); 
    System.out.println("author count = " + authorCount);
 }
}

 

 

check web page title and url

The following expected conditions can be used for checking the web page title and url.

1. titleContains

It defines an expectation for checking that the title contains a case-sensitive substring.

ExpectedCondition titleContains(java.lang.String title)

2. titleIs

ExpectedCondition titleIs(java.lang.String title)

It defines an expectation for checking the title of a page.

3. urlContains

ExpectedCondition urlContains(java.lang.String fraction)

It defines an expectation for the URL of the current page to contain specific text.

4. urlToBe

It defines an expectation for the URL of the current page to be a specific url.

ExpectedCondition urlToBe(java.lang.String url)

5. urlMatches

ExpectedCondition urlMatches(java.lang.String regex)

It defines an expectation for the URL to match a specific regular expression

 

Example

Let’s improve the first code sample by checking that the page titles and urls are correct.

import static org.junit.Assert.*;
import static org.junit.Assert.*;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import org.openqa.selenium.By;
import org.openqa.selenium.WebDriver;
import org.openqa.selenium.WebElement;
import org.openqa.selenium.chrome.ChromeDriver;
import org.openqa.selenium.support.ui.ExpectedConditions;
import org.openqa.selenium.support.ui.WebDriverWait;

public class TestClass { 
  WebDriver driver; 
  WebDriverWait wait; 

  //this is where chromedriver.exe should be 
  String driverPath = "c:/browserdrivers/chromedriver.exe"; 

  @Before 
  public void setUp() { 
    System.setProperty("webdriver.chrome.driver",            
                      driverPath); 
    driver = new ChromeDriver(); 
    wait = new WebDriverWait(driver, 10); 
  } 

  @After 
  public void tearDown() { 
    driver.quit(); 
  } 

  @Test 
  public void clickElementWithExplicitWait() 
       throws InterruptedException { 

    //open site 
    driver.get("http://www.vpl.ca"); 

    //check if the home title is correct String 
    expectedHomeTitle = "Vancouver Public Library |"; 
    boolean isTitleCorrect = wait.until 
                                (ExpectedConditions .
                                   titleIs(expectedHomeTitle)); 
    assertTrue(isTitleCorrect == true); 

    //finds the search box 
    By searchBoxId = By.id("edit-search"); 
    WebElement searchBox = wait.until(
                               ExpectedConditions .
                                 elementToBeClickable(searchBoxId)); 

    //types in the search box 
    searchBox.click(); 
    searchBox.clear(); 
    searchBox.sendKeys("java"); 

    //finds the search button 
    By searchButtonId = By.id("edit-submit"); 
    WebElement searchButton = wait.until(
                                 ExpectedConditions .
                                    visibilityOfElementLocated 
                                        (searchButtonId)); 

    //clicks the search button 
    searchButton.click(); 

    //check if the results url is correct 
    String expectedResultsUrl = "https://vpl.bibliocommons.com/search"; 
    boolean isUrlCorrect = wait.until (
                             ExpectedConditions .
                                urlContains(expectedResultsUrl)); 
    assertTrue(isUrlCorrect == true); 

    //delay so you can see whats happening in the browser 
    Thread.sleep(10000); 
 }
}

check elements status

All previous expected conditions are very common.

There are also other expected conditions that can be used but are less common.

No complete code samples are provided for them but feel free to try them out.

1. elementSelectionStateToBe

ExpectedCondition elementSelectionStateToBe(By locator, boolean selected)
ExpectedCondition elementSelectionStateToBe(WebElement element, boolean selected)

It defines an expectation for checking if the given element is selected.

Example

WebDriverWait wait = new WebDriverWait(driver, 10);
assertTrue(wait.until(ExpectedConditions.
                      elementSelectionStateToBe(locator, true)));

2. elementToBeSelected

ExpectedCondition elementToBeSelected(By locator)
ExpectedCondition elementToBeSelected(WebElement element)

It defines an expectation for checking if the given element is selected.

Example

WebDriverWait wait = new WebDriverWait(driver, 10);
assertTrue(wait.until(ExpectedConditions.
                      elementToBeSelected(locator)));

3. invisibilityOfElementLocated

ExpectedCondition invisibilityOfElementLocated(By locator)

It defines an expectation for checking that an element is either invisible or not present on the DOM.

Example

 

WebDriverWait wait = new WebDriverWait(driver, 10);
assertTrue(wait.until(ExpectedConditions.
                      invisibilityOfElementLocated(locator)));

4. invisibilityOfElementWithText

ExpectedCondition invisibilityOfElementWithText(By locator, java.lang.String text)

It defines an expectation for checking that an element with text is either invisible or not present on the DOM.

Example

WebDriverWait wait = new WebDriverWait(driver, 10);
assertTrue(wait.until(ExpectedConditions.
                      invisibilityOfElementWithText(locator, text)));

5. stalenessOf

ExpectedCondition stalenessOf(WebElement element)

Wait until an element is no longer attached to the DOM.

Example

WebDriverWait wait = new WebDriverWait(driver, 10);
assertTrue(wait.until(ExpectedConditions.
                      stalenessOf(element)));

6. textToBePresentInElement

ExpectedCondition textToBePresentInElement(WebElement element, java.lang.String text)
ExpectedCondition textToBePresentInElementLocated(By locator, java.lang.String text)

It defines an expectation for checking if the given text is present in the element that matches the given locator.

Example

WebDriverWait wait = new WebDriverWait(driver, 10);
assertTrue(wait.until(ExpectedConditions.
                      textToBePresentInElementLocated(locator, keyword)));

7. textToBePresentInElementValue

ExpectedCondition textToBePresentInElementValue(By locator, java.lang.String text)
ExpectedCondition textToBePresentInElementValue(WebElement element, java.lang.String text)

It defines an expectation for checking if the given text is present in the specified elements value attribute.

Example

WebDriverWait wait = new WebDriverWait(driver, 10);
assertTrue(wait.until(ExpectedConditions.
                      textToBePresentInElementValue(locator, keyword)));

 

RELATED ARTICLE

How to create custom expected conditions in Selenium

 

WebDriver emulates user actions on a page.

When the automation script is executed, the following steps happen:

  • for each Selenium command, a HTTP request is created and sent to the browser driver
  • the browser driver uses a HTTP server for getting the HTTP requests
  • the HTTP server determines the steps needed for implementing the Selenium command 
  • the implementation steps are executed on the browser
  • the execution status is sent back to the HTTP server
  • the HTTP server sends the status back to the automation script

 

Selenium Client & WebDriver Language Bindings


Multiple languages are available for writing test automation scripts using the Selenium WebDriver framework.

The most popular are Java, Ruby, C# and Python.

Test automation scripts use Selenium commands for interacting with the elements of a site:

 

package org.openqa.selenium.example;

import org.openqa.selenium.By;
import org.openqa.selenium.WebDriver;
import org.openqa.selenium.WebElement;
import org.openqa.selenium.firefox.FirefoxDriver;
import org.openqa.selenium.support.ui.ExpectedCondition;
import org.openqa.selenium.support.ui.WebDriverWait;

public class Selenium2Example {
public static void main(String[] args) {
// Create a new instance of the Firefox driver
// Notice that the remainder of the code relies on the interface,
// not the implementation.
WebDriver driver = new FirefoxDriver();

// And now use this to visit Google
driver.get(http://www.google.com&#8221;);
// Alternatively the same thing can be done like this
// driver.navigate().to(“http://www.google.com&#8221;);

// Find the text input element by its name
WebElement element = driver.findElement(By.name(“q”));

// Enter something to search for
element.sendKeys(“Cheese!”);

// Now submit the form. WebDriver will find the form for us from the element
element.submit();

// Check the title of the page
System.out.println(“Page title is: “ + driver.getTitle());

// Google’s search is rendered dynamically with JavaScript.
// Wait for the page to load, timeout after 10 seconds
(new WebDriverWait(driver, 10)).until(new ExpectedCondition<Boolean>() {
public Boolean apply(WebDriver d) {
return d.getTitle().toLowerCase().startsWith(“cheese!”);
}
});

// Should see: “cheese! – Google Search”
System.out.println(“Page title is: “ + driver.getTitle());

//Close the browser
driver.quit();
}
}

 


selenium free course

 

The browser driver implements Selenium commands that emulate all types of actions that users execute typically on a website:

  1. open a web page
  2. maximize the browser
  3. navigate through the browser history
  4. click buttons and links
  5. select values in lists and dropdown lists
  6. type text in textboxes
  7. get status of web elements
  8. get information of web elements (value, attributes, position)

The browser driver is the same regardless of the language used for automation.

So, in order to use the browser driver from any language, a language binding was created for each language so that

  • If you want to use the browser driver in Java, use the Java bindings for Selenium WebDriver
  • If you want to use the driver in C#, Ruby or Python, use the binding for that language

All language bindings are available for download on the Selenium HQ site on the Selenium WebDriver & Client Language Bindings section.

For Java, the bindings are included in the selenium-java-2.47.1.zip file:

selenium-java-2.47.1.jar (Java bindings)
selenium-java-2.47.1-srcs.jar

 

HTTP request is created for each Selenium command

 

For each Selenium command of the automation script, a http request with a specific path is created.

 

 

When the automation script is executed, the first http request generates a new session that is specific to the browser where the automation scripts run.

The session id will be used for the http requests that correspond to all other Selenium commands from the automation script.

The details of the Create New Session http request are below:

POST /session

Create a new session. The server should attempt to create a session that most closely matches the desired and required capabilities. Required capabilities have higher priority than desired capabilities and must be set for the session to be created.
JSON Parameters:
desiredCapabilities – {object} An object describing the session’s desired capabilities.
requiredCapabilities – {object} An object describing the session’s required capabilities (Optional).
Returns:
{object} An object describing the session’s capabilities.
Potential Errors:
SessionNotCreatedException – If a required capability could not be set.

 

1. HTTP REQUEST TYPES

HTTP requests are GET or POST requests

  • GET requests

    Get requests are generated usually for Selenium interrogation commands (commands that get information from web elements) such as

    • getText()
    • getAttribute()
    • isDisplayed()
    • isSelected()

 

EXAMPLEhttp request for checking if an element is displayed:

URL
GET /session/:sessionId/element/:id/displayed

Purpose
Determine if an element is currently displayed.

URL Parameters
:sessionId – ID of the session to route the command to.
:id – ID of the element to route the command to.

Returns
{boolean} Whether the element is displayed.

 

  • POST requests 

    Post requests are generated usually for Selenium manipulation commands (commands that interact with web elements) such as

    • get()
    • findElement()
    • findElements()
    • click()

EXAMPLE: http request for clicking an element:

URL
POST /session/:sessionId/element/:id/click

Purpose
Click on an element.

URL Parameters
:sessionId – ID of the session to route the command to.
:id – ID of the element to route the command to.

The communication between the client (computer that runs the test automation scripts) and the browser driver uses the HTTP and JSON wire protocol.

 


selenium free course

2. HTTP REQUEST PARAMETERS

The HTTP requests use the following types of parameters:

  • URL parameters

EXAMPLE: http request for clicking an element

URL
POST /session/:sessionId/element/:id/click

URL Parameters
:sessionId – ID of the session to route the command to.
:id – ID of the element to route the command to.

  • JSON parameters

EXAMPLE: http request for finding an element

URL
POST /session/:sessionId/element

URL Parameters
:sessionId – ID of the session to route the command to.

JSON Parameters
using – {string} The locator strategy to use.
value – {string} The The search target.

 

3. HTTP REQUEST RESULT

The result of a HTTP request can be:
  • a value

EXAMPLE: http request for getting the title of the current page

URL
GET /session/:sessionId/title

URL Parameters
:sessionId – ID of the session to route the command to.

Returns
{string} The current page title.

  • a JSON object

EXAMPLE: http request for finding an element

URL
POST /session/:sessionId/element

URL Parameters
:sessionId – ID of the session to route the command to.

JSON Parameters
using – {string} The locator strategy to use.
value – {string} The The search target.

Returns
{ELEMENT:string} A WebElement JSON object for the located element.

 

Browser Driver

 

The browser driver is implemented in a EXE file or a browser extension:

  • CHROME browser
    1. the browser driver is included in the chromedriver.exe file
    2. download it from here
  • INTERNET EXPLORER browser
    1. the browser driver is included in the internetexplorer.exe file
    2. download it from the Internet Explorer Driver Server section of the Selenium Downloads page
  • FIREFOX browser
    1. the browser driver is a firefox extension included in the Selenium Client & WebDriver Language binding file

 

 

The browser driver uses an HTTP SERVER which waits continuously for new Selenium commands.

It has the following purposes:

  • read HTTP requests coming from the client (client = computer that executes the test automation scripts)
  • determines the series of steps needed for implementing the Selenium command
  • sends the implementation steps to the browser
  • gets the execution status from the browser
  • send the execution status back to the client

For example, see below the implementation steps for the http request that corresponds to the getTitle() Selenium WebDriver command:

GET /session/{session id}/title

The Get Title command returns the document title of the current top-level browsing context, equivalent to calling window.top.document.title.

The remote end steps are:

  1. If the current top-level browsing context is no longer open, return error with error code no such window.
  2. Handle any user prompts and return its value if it is an error.
  3. Let title be the value of the current top-level browsing context’s active document’s title.
  4. Let data be a new JSON Object.
  5. Set the property “value” of data to the value of title.
  6. Return success with data data.

 


selenium free course

So, the process of executing the test scripts consists in


1. write the code in the programming language

2. use the language binding for including Selenium commands in the test scripts

3. the test automation script execution is started

4. the browser driver is created

5. the HTTP server starts listening for Selenium commands HTTP requests

6. for each Selenium command from the test script, a HTTP request is created

7. the HTTP request is sent to the HTTP server

8. the HTTP server determines the steps needed for implementing the Selenium command

9. the implementation steps are sent to the browser 

10. as soon as the execution status is returned from the browser, the HTTP server send the execution status back to the test script

How was the Selenium name chosen?


Selenium was so named because Huggins, dissatisfied with testing tools on the market, was seeking a name that would position the product as an alternative to the QuickTest Professional commercial testing software built by a company called Mercury Interactive:

 

The name, Selenium, was selected because selenium mineral supplements serve as a cure for mercury poisoning, Huggins explained.




From techworld.com