001/* 002 * (C) Copyright 2006-2011 Nuxeo SA (http://nuxeo.com/) and others. 003 * 004 * Licensed under the Apache License, Version 2.0 (the "License"); 005 * you may not use this file except in compliance with the License. 006 * You may obtain a copy of the License at 007 * 008 * http://www.apache.org/licenses/LICENSE-2.0 009 * 010 * Unless required by applicable law or agreed to in writing, software 011 * distributed under the License is distributed on an "AS IS" BASIS, 012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 013 * See the License for the specific language governing permissions and 014 * limitations under the License. 015 * 016 * Contributors: 017 */ 018 019package org.openqa.selenium.support.ui; 020 021import static java.util.concurrent.TimeUnit.MILLISECONDS; 022import static java.util.concurrent.TimeUnit.SECONDS; 023 024import org.openqa.selenium.NotFoundException; 025import org.openqa.selenium.WebDriver; 026import org.openqa.selenium.WebDriverException; 027 028import com.google.common.base.Function; 029 030/** 031 * An implementation of the Wait interface that makes use of WebDriver. The expected usage is in conjunction with the 032 * {@link ExpectedCondition} interface. 033 * <p> 034 * Because waiting for elements to appear on a page is such a common use-case, this class will silently swallow 035 * NotFoundException whilst waiting. 036 */ 037public class WebDriverWait implements Wait<WebDriver> { 038 private final Clock clock; 039 040 private final WebDriver driver; 041 042 private final long timeOutInMillis; 043 044 private final long sleepTimeOut; 045 046 private static final long DEFAULT_SLEEP_TIMEOUT = 500; 047 048 /** 049 * @param driver The WebDriver instance to pass to the expected conditions 050 * @param timeOutInSeconds The timeout in seconds when an expectation is called 051 */ 052 public WebDriverWait(WebDriver driver, long timeOutInSeconds) { 053 this(new SystemClock(), driver, timeOutInSeconds, DEFAULT_SLEEP_TIMEOUT); 054 } 055 056 /** 057 * @param clock The clock to use when measuring the timeout 058 * @param driver The WebDriver instance to pass to the expected conditions 059 * @param timeOutInSeconds The timeout in seconds when an expectation is 060 * @param sleepTimeOut The timeout used whilst sleeping. Defaults to 500ms called 061 */ 062 protected WebDriverWait(Clock clock, WebDriver driver, long timeOutInSeconds, long sleepTimeOut) { 063 this.clock = clock; 064 this.driver = driver; 065 this.timeOutInMillis = SECONDS.toMillis(timeOutInSeconds); 066 this.sleepTimeOut = sleepTimeOut; 067 } 068 069 @Override 070 public <T> T until(Function<WebDriver, T> isTrue) { 071 long end = clock.laterBy(timeOutInMillis); 072 NotFoundException lastException = null; 073 074 while (clock.isNowBefore(end)) { 075 try { 076 T value = isTrue.apply(driver); 077 078 if (value != null && Boolean.class.equals(value.getClass())) { 079 if (Boolean.TRUE.equals(value)) { 080 return value; 081 } 082 } else if (value != null) { 083 return value; 084 } 085 } catch (NotFoundException e) { 086 // Common case in many conditions, so swallow here, but be ready to 087 // rethrow if it the element never appears. 088 lastException = e; 089 } 090 sleep(); 091 } 092 093 throwTimeoutException( 094 String.format("Timed out after %d seconds", SECONDS.convert(timeOutInMillis, MILLISECONDS)), 095 lastException); 096 097 throw new IllegalStateException("'throwTimeoutException' should have thrown an exception!"); 098 } 099 100 /** 101 * Override this method to throw an exception that is idiomatic for a given test infrastructure. E.g. JUnit4 should 102 * throw an {@link AssertionError} 103 */ 104 protected void throwTimeoutException(String message, Exception lastException) { 105 throw new TimeoutException(message, lastException); 106 } 107 108 private void sleep() { 109 try { 110 Thread.sleep(sleepTimeOut); 111 } catch (InterruptedException e) { 112 Thread.currentThread().interrupt(); 113 throw new WebDriverException(e); 114 } 115 } 116}