View Javadoc

1   package com.atlassian.webdriver.utils.element;
2   
3   import com.atlassian.pageobjects.PageBinder;
4   import com.google.common.base.Function;
5   import com.google.common.base.Predicate;
6   import org.hamcrest.Matcher;
7   import org.openqa.selenium.TimeoutException;
8   import org.openqa.selenium.WebDriver;
9   import org.openqa.selenium.WebElement;
10  import org.openqa.selenium.support.ui.FluentWait;
11  import org.openqa.selenium.support.ui.WebDriverWait;
12  
13  import javax.annotation.Nonnull;
14  import javax.inject.Inject;
15  import java.util.concurrent.TimeUnit;
16  
17  import static com.google.common.base.Preconditions.checkArgument;
18  import static com.google.common.base.Preconditions.checkNotNull;
19  
20  /**
21   * <p/>
22   * A component that can be used to wait for certain conditions to happen on the tested page.
23   *
24   * <p/>
25   * The conditions are expressed as a generic {@link Function} from {@link WebDriver} to {@code boolean}.
26   * {@link ElementConditions} contains factory methods to easily create some commonly used conditions.
27   *
28   * <p/>
29   * The {@link #DEFAULT_TIMEOUT} and {@link #DEFAULT_TIMEOUT_UNIT} specify the default timeout used when
30   * no explicit timeout is provided by the client, which is currently 30 seconds. Clients are encouraged to use
31   * their own timeout specific to the situation.
32   *
33   * <p/>
34   * NOTE: the default poll interval used by this class is as in the underlying {@link WebDriverWait} and is currently
35   * 500ms (subject to change as the underlying {@link WebDriverWait} implementation changes. This may be generally
36   * acceptable, but may not be granular enough for some scenarios (e.g. performance testing).
37   *
38   * <p/>
39   * Example usage:
40   * <pre>
41   *     {@code
42   *     @literal @Inject private WebDriverPoller poller;
43   *
44   *     // ...
45   *     // wait for 5s for a 'my-element' to be present on the page
46   *     poller.waitUntil(ElementConditions.isPresent(By.id("my-element")), 5);
47   *     }
48   * </pre>
49   *
50   * <p/>
51   * As of 2.3, {@code WebDriverPoller} also supports waiting for {@code WebElement}-specific predicates and matchers,
52   * which require the web element to be already located.
53   * <p/>
54   * This component can be injected into page objects running within a {@link PageBinder} context.
55   *
56   * <p/>
57   * For more sophisticated polling/waiting toolkit, check the {@code PageElement} API in the
58   * atlassian-pageobjects-elements module.
59   *
60   * @since 2.2
61   * @see ElementConditions
62   * @see WebDriverWait
63   */
64  public final class WebDriverPoller
65  {
66      public static final long DEFAULT_TIMEOUT = 30;
67      public static final TimeUnit DEFAULT_TIMEOUT_UNIT = TimeUnit.SECONDS;
68  
69      private final WebDriver webDriver;
70      private final TimeUnit timeUnit;
71      private final long timeout;
72  
73      @Inject
74      public WebDriverPoller(@Nonnull WebDriver webDriver)
75      {
76          this(webDriver, DEFAULT_TIMEOUT, DEFAULT_TIMEOUT_UNIT);
77      }
78  
79      public WebDriverPoller(@Nonnull WebDriver webDriver, long timeout, @Nonnull TimeUnit timeUnit)
80      {
81          checkArgument(timeout > 0, "Timeout must be >0");
82          this.webDriver = checkNotNull(webDriver, "webDriver");
83          this.timeout = timeout;
84          this.timeUnit = checkNotNull(timeUnit, "timeUnit");
85      }
86  
87      @Nonnull
88      public WebDriverPoller withDefaultTimeout(long timeout, @Nonnull TimeUnit timeUnit)
89      {
90          return new WebDriverPoller(webDriver, timeout, timeUnit);
91      }
92  
93      /**
94       * Wait until {@literal condition} is {@literal true}, up to the default timeout. The default timeout depends
95       * on the arguments supplied while creating this {@code WebDriverPoller}.
96       *
97       * @param condition condition that must evaluate to {@literal true}
98       * @throws TimeoutException if the condition does not come true before the timeout expires
99       * @see #DEFAULT_TIMEOUT
100      * @see #DEFAULT_TIMEOUT_UNIT
101      */
102     public void waitUntil(@Nonnull Function<WebDriver, Boolean> condition)
103     {
104         waitUntil(condition, timeout, timeUnit);
105     }
106 
107     /**
108      * Wait until {@literal condition} up to the {@literal timeoutInSeconds}.
109      *
110      * @param condition condition that must evaluate to {@literal true}
111      * @param timeoutInSeconds timeout in seconds to wait for {@literal condition} to come {@code true}
112      * @throws TimeoutException if the condition does not come true before the timeout expires
113      */
114     public void waitUntil(@Nonnull Function<WebDriver, Boolean> condition, long timeoutInSeconds)
115     {
116         new WebDriverWait(webDriver, timeoutInSeconds).until(condition);
117     }
118 
119     /**
120      * Wait until {@literal condition} up to the {@literal timeout} specified by {@literal unit}.
121      *
122      * @param condition condition that must evaluate to {@literal true}
123      * @param timeout timeout to wait for {@literal condition} to come true
124      * @param unit unit of the {@literal timeout}
125      * @throws TimeoutException if the condition does not come true before the timeout expires
126      */
127     public void waitUntil(@Nonnull Function<WebDriver, Boolean> condition, long timeout, @Nonnull TimeUnit unit)
128     {
129         waitUntil(condition, unit.toSeconds(timeout));
130     }
131 
132     /**
133      * Wait until {@literal condition} is {@code true} for {@code element}, up to the default timeout. The default
134      * timeout depends on the arguments supplied while creating this {@code WebDriverPoller}.
135      *
136      * @param element the element to examine
137      * @param condition condition that must evaluate to {@literal true}, expressed by a {@link Predicate}
138      * @throws TimeoutException if the condition does not come true before the timeout expires
139      * @since 2.3
140      *
141      * @see #DEFAULT_TIMEOUT
142      * @see #DEFAULT_TIMEOUT_UNIT
143      */
144     public void waitUntil(@Nonnull WebElement element, @Nonnull Predicate<WebElement> condition)
145     {
146         waitUntil(element, condition, timeout, timeUnit);
147     }
148 
149     /**
150      * Wait until {@literal condition} is {@code true} for {@code element}, up to the {@literal timeoutInSeconds}.
151      *
152      * @param element the element to examine
153      * @param condition condition that must evaluate to {@literal true}, expressed by a {@link Predicate}
154      * @param timeoutInSeconds timeout in seconds to wait for {@literal condition} to come {@code true}
155      * @throws TimeoutException if the condition does not come true before the timeout expires
156      * @since 2.3
157      */
158     public void waitUntil(@Nonnull WebElement element, @Nonnull Predicate<WebElement> condition, long timeoutInSeconds)
159     {
160         waitUntil(element, condition, timeoutInSeconds, TimeUnit.SECONDS);
161     }
162 
163     /**
164      * Wait until {@literal condition} is {@code true} for {@code element}, up to the {@literal timeout} specified
165      * by {@literal unit}.
166      *
167      * @param element the element to examine
168      * @param condition condition that must evaluate to {@literal true}, expressed by a {@link Predicate}
169      * @param timeout timeout to wait for {@literal condition} to come true
170      * @param unit unit of the {@literal timeout}
171      * @throws TimeoutException if the condition does not come true before the timeout expires
172      * @since 2.3
173      */
174     public void waitUntil(@Nonnull WebElement element, @Nonnull Predicate<WebElement> condition,
175                           long timeout, TimeUnit unit)
176     {
177         new FluentWait<WebElement>(checkNotNull(element, "element")).withTimeout(timeout, unit).until(condition);
178     }
179 
180     /**
181      * Wait until {@literal condition} is {@code true} for {@code element}, up to the default timeout. The default
182      * timeout depends on the arguments supplied while creating this {@code WebDriverPoller}.
183      *
184      * @param element the element to examine
185      * @param condition condition that must evaluate to {@code true}, expressed by a {@code Matcher}
186      * @throws TimeoutException if the condition does not come true before the timeout expires
187      * @since 2.3
188      *
189      * @see #DEFAULT_TIMEOUT
190      * @see #DEFAULT_TIMEOUT_UNIT
191      */
192     public void waitUntil(@Nonnull WebElement element, @Nonnull Matcher<? super WebElement> condition)
193     {
194         waitUntil(element, condition, timeout, timeUnit);
195     }
196 
197     /**
198      * Wait until {@code condition} is {@code true} for {@code element}, up to the {@code timeoutInSeconds}.
199      *
200      * @param element the element to examine
201      * @param condition condition that must evaluate to {@code true}, expressed by a {@link Matcher}
202      * @param timeoutInSeconds timeout in seconds to wait for {@code condition} to come {@code true}
203      * @throws TimeoutException if the condition does not come true before the timeout expires
204      * @since 2.3
205      */
206     public void waitUntil(@Nonnull WebElement element, @Nonnull Matcher<? super WebElement> condition,
207                           long timeoutInSeconds)
208     {
209         waitUntil(element, condition, timeoutInSeconds, TimeUnit.SECONDS);
210     }
211 
212     /**
213      * Wait until {@literal condition} is {@code true} for {@code element}, up to the {@code timeout} specified
214      * by {@code unit}.
215      *
216      * @param element the element to examine
217      * @param condition condition that must evaluate to {@code true}, expressed by a {@link Matcher}
218      * @param timeout timeout to wait for {@code condition} to come true
219      * @param unit unit of the {@code timeout}
220      * @throws TimeoutException if the condition does not come true before the timeout expires
221      * @since 2.3
222      */
223     public void waitUntil(@Nonnull WebElement element, @Nonnull Matcher<? super WebElement> condition,
224                           long timeout, TimeUnit unit)
225     {
226         waitUntil(element, newMatcherPredicate(condition), timeout, unit);
227     }
228 
229     static <E> Predicate<E> newMatcherPredicate(final Matcher<? super E> filter)
230     {
231         return new Predicate<E>()
232         {
233             @Override
234             public boolean apply(E element)
235             {
236                 return filter.matches(element);
237             }
238         };
239     }
240 }