2

It appears that the SafariDriver for Selenium doesn't wait for web pages to load. My test is as follows:

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.chrome.ChromeDriver;
import org.openqa.selenium.safari.SafariDriver;

public class SafariTest {

    private WebDriver driver;

    @Before
    public void setUp() throws Exception {
        driver = new SafariDriver();
    }

    @After
    public void tearDown() throws Exception {
        driver.close();
    }

    @Test
    public void testGoogleSearch() {
        driver.get("http://duckduckgo.com");
        driver.findElement(By.id("search_form_input_homepage")).sendKeys("Hello World");
        driver.findElement(By.id("search_button_homepage")).click();
        driver.findElement(By.linkText("Images")).click();
    }
}

If you run this with ChromeDriver, or FirefoxDriver, it functions as it should, i.e. it searches for "Hello World", then on the results page, it goes to the image results.

With SafariDriver, it fails with:

org.openqa.selenium.NoSuchElementException: An element could not be located on the page using the given search parameters. (WARNING: The server did not provide any stacktrace information)

The element that could not be found being "Images", since the page hasn't loaded before it ran that statement.

Is this expected behavior? Am I supposed to special case for Safari?

James Affleck
  • 293
  • 1
  • 5
  • 10
  • It could be that safari is just slower in some regard that causes issues with this page. Generally speaking, you should be using **[explicit waits](https://stackoverflow.com/questions/10404160/when-to-use-explicit-wait-vs-implicit-wait-in-selenium-webdriver)**. Or I guess **Implicit waits** if you want to lock yourself into not having flexibility to use **explicit waits**, since the selenium documentation warns against mixing them due to unexpected wait times. – mrfreester Aug 02 '17 at 20:54
  • @mrfreester Why should explicit waits be preferred? –  Aug 03 '17 at 05:24
  • **Implicit waits**, if used, should only be set once at the beginning of your test suite. However, often people will change them in the middle of a test suite, which causes unpredictability in future test wait times and subtle bugs that are hard to track down. **Implicit waits** also only wait for elements to __exist__, which doesn't always mean the element is ready to be interacted with. With **Explicit waits**, you can clearly say that you're waiting for it to be **visible**, **clickable**, or whatever else you want, making your test more clear and stable. – mrfreester Aug 03 '17 at 14:59

2 Answers2

0

Basically Selenium blows up when you try to 'click' or 'sendKeys' on/into something that doesn't exist on the page yet.
(I'm sure there's a default implicit wait, but I'm not sure what it is). Basically it's up to you to determine how flexible you want tests to be for waiting for things before they fail. I hope this helps.

Explicit wait example:

@Test
public void testGoogleSearch() {
    driver.get("http://duckduckgo.com");
    By searchInputBy = By.id("search_form_input_homepage");
    WebDriverWait wait = new WebDriverWait(driver, 10);
    wait.until(ExpectedConditions.visibilityOfElementLocated(searchInputBy));
    driver.findElement(searchInputBy).sendKeys("Hello World");
    driver.findElement(By.id("search_button_homepage")).click();
    wait = new WebDriverWait(driver, 10);
    By imagesBy = By.linkText("Images");
    wait.until(ExpectedConditions.elementToBeClickable(imagesBy));
    driver.findElement(imagesBy).click();
} 

Implicit wait example:

@Test
public void testGoogleSearch() {
    driver.manage().timeouts().implicitlyWait(10,TimeUnit.SECONDS) ;
    driver.get("http://duckduckgo.com");
    driver.findElement(By.id("search_form_input_homepage")).sendKeys("Hello World");
    driver.findElement(By.id("search_button_homepage")).click();
    driver.findElement(By.linkText("Images")).click();
}

You also have the option of using fluent waits which give you more control over explicit waits so you can tell it to ignore certain exceptions, but they're more verbose.

I think creating a library of static methods to do the heavy lifting of fluent waits is a better more readable, less bug-prone way to write tests though.

FluentWait Javadoc

Also a great answer explaining waits in more detail.

James Affleck
  • 293
  • 1
  • 5
  • 10
  • Have you tried your examples? Implicit wait does not work with Safari. Explicit wait should not be necessary (and isn't necessary, again, for all browsers _except_ Safari) because Selenium is supposed to wait for page loads to finish before each step. –  Aug 03 '17 at 05:23
  • I just looked at my code and realized I forgot to pass it the parameter of the locator to wait for. Should work now. – James Affleck Aug 03 '17 at 19:37
0

The root cause is your code doesn't wait for the search results. You an use WebDriverWait along with ExpectedConditions to wait for the images link. See an example below.

@Test
public void testGoogleSearch() {
    driver.get("http://duckduckgo.com");
    driver.findElement(By.id("search_form_input_homepage")).sendKeys("Hello World");
    driver.findElement(By.id("search_button_homepage")).click();
    WebDriverWait waiter = new WebDriverWait(driver,20);
    WebElement imagesLink = waiter.until(ExpectedConditions.elementToBeClickable(By.linkText("Images")));
    imagesLink.click();
}
Buaban
  • 5,029
  • 1
  • 17
  • 33