Skip to content

Commit fef98c1

Browse files
Olivier SCHNEIDERbarancev
authored andcommitted
Add FluentWait.withMessage with string supplier
Add a withMethod(Supplier<String> messgaeSupplier) to be able to evaluate the message when the wait is over, thus enabling developers to show more info on the context of failure through closures. Internally, the FluentWait class is using a supplier. withMessage(String message) method kept for backward compatibility creates a supplier that return the given string. Signed-off-by: Alexei Barantsev <[email protected]>
1 parent 5eb8c3e commit fef98c1

File tree

2 files changed

+108
-44
lines changed

2 files changed

+108
-44
lines changed

java/client/src/org/openqa/selenium/support/ui/FluentWait.java

Lines changed: 33 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -17,22 +17,24 @@
1717

1818
package org.openqa.selenium.support.ui;
1919

20+
import static com.google.common.base.Preconditions.checkNotNull;
21+
import static java.util.concurrent.TimeUnit.MILLISECONDS;
22+
import static java.util.concurrent.TimeUnit.SECONDS;
23+
2024
import com.google.common.base.Function;
2125
import com.google.common.base.Predicate;
26+
import com.google.common.base.Supplier;
2227
import com.google.common.base.Throwables;
2328
import com.google.common.collect.ImmutableList;
2429
import com.google.common.collect.Lists;
30+
2531
import org.openqa.selenium.TimeoutException;
2632
import org.openqa.selenium.WebDriverException;
2733

2834
import java.util.Collection;
2935
import java.util.List;
3036
import java.util.concurrent.TimeUnit;
3137

32-
import static com.google.common.base.Preconditions.checkNotNull;
33-
import static java.util.concurrent.TimeUnit.MILLISECONDS;
34-
import static java.util.concurrent.TimeUnit.SECONDS;
35-
3638
/**
3739
* An implementation of the {@link Wait} interface that may have its timeout and polling interval
3840
* configured on the fly.
@@ -75,7 +77,12 @@ public class FluentWait<T> implements Wait<T> {
7577

7678
private Duration timeout = FIVE_HUNDRED_MILLIS;
7779
private Duration interval = FIVE_HUNDRED_MILLIS;
78-
private String message = null;
80+
private Supplier<String> messageSupplier = new Supplier<String>() {
81+
@Override
82+
public String get() {
83+
return null;
84+
}
85+
};
7986

8087
private List<Class<? extends Throwable>> ignoredExceptions = Lists.newLinkedList();
8188

@@ -116,8 +123,24 @@ public FluentWait<T> withTimeout(long duration, TimeUnit unit) {
116123
* @param message to be appended to default.
117124
* @return A self reference.
118125
*/
119-
public FluentWait<T> withMessage(String message) {
120-
this.message = message;
126+
public FluentWait<T> withMessage(final String message) {
127+
this.messageSupplier = new Supplier<String>() {
128+
@Override
129+
public String get() {
130+
return message;
131+
}
132+
};
133+
return this;
134+
}
135+
136+
/**
137+
* Sets the message to be evaluated and displayed when time expires.
138+
*
139+
* @param messageSupplier to be evaluated on failure and appended to default.
140+
* @return A self reference.
141+
*/
142+
public FluentWait<T> withMessage(Supplier<String> messageSupplier) {
143+
this.messageSupplier = messageSupplier;
121144
return this;
122145
}
123146

@@ -221,6 +244,9 @@ public <V> V until(Function<? super T, V> isTrue) {
221244
// Check the timeout after evaluating the function to ensure conditions
222245
// with a zero timeout can succeed.
223246
if (!clock.isNowBefore(end)) {
247+
String message = messageSupplier != null ?
248+
messageSupplier.get() : null;
249+
224250
String toAppend = message == null ?
225251
" waiting for " + isTrue.toString() : ": " + message;
226252

java/client/test/org/openqa/selenium/support/ui/FluentWaitTest.java

Lines changed: 75 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@
2626
import static org.mockito.Mockito.when;
2727

2828
import com.google.common.base.Function;
29+
import com.google.common.base.Supplier;
2930

3031
import org.junit.Before;
3132
import org.junit.Test;
@@ -42,14 +43,18 @@
4243
import java.util.concurrent.TimeUnit;
4344

4445
@RunWith(JUnit4.class)
45-
public class FluentWaitTest{
46+
public class FluentWaitTest {
4647

4748
private static final Object ARBITRARY_VALUE = new Object();
4849

49-
@Mock private WebDriver mockDriver;
50-
@Mock private ExpectedCondition<Object> mockCondition;
51-
@Mock private Clock mockClock;
52-
@Mock private Sleeper mockSleeper;
50+
@Mock
51+
private WebDriver mockDriver;
52+
@Mock
53+
private ExpectedCondition<Object> mockCondition;
54+
@Mock
55+
private Clock mockClock;
56+
@Mock
57+
private Sleeper mockSleeper;
5358

5459
@Before
5560
public void createMocks() {
@@ -63,9 +68,9 @@ public void shouldWaitUntilReturnValueOfConditionIsNotNull() throws InterruptedE
6368
when(mockCondition.apply(mockDriver)).thenReturn(null, ARBITRARY_VALUE);
6469

6570
Wait<WebDriver> wait = new FluentWait<WebDriver>(mockDriver, mockClock, mockSleeper)
66-
.withTimeout(0, TimeUnit.MILLISECONDS)
67-
.pollingEvery(2, TimeUnit.SECONDS)
68-
.ignoring(NoSuchElementException.class, NoSuchFrameException.class);
71+
.withTimeout(0, TimeUnit.MILLISECONDS)
72+
.pollingEvery(2, TimeUnit.SECONDS)
73+
.ignoring(NoSuchElementException.class, NoSuchFrameException.class);
6974

7075
assertEquals(ARBITRARY_VALUE, wait.until(mockCondition));
7176
verify(mockSleeper, times(1)).sleep(new Duration(2, TimeUnit.SECONDS));
@@ -78,9 +83,9 @@ public void shouldWaitUntilABooleanResultIsTrue() throws InterruptedException {
7883
when(mockCondition.apply(mockDriver)).thenReturn(false, false, true);
7984

8085
Wait<WebDriver> wait = new FluentWait<WebDriver>(mockDriver, mockClock, mockSleeper)
81-
.withTimeout(0, TimeUnit.MILLISECONDS)
82-
.pollingEvery(2, TimeUnit.SECONDS)
83-
.ignoring(NoSuchElementException.class, NoSuchFrameException.class);
86+
.withTimeout(0, TimeUnit.MILLISECONDS)
87+
.pollingEvery(2, TimeUnit.SECONDS)
88+
.ignoring(NoSuchElementException.class, NoSuchFrameException.class);
8489

8590
assertEquals(true, wait.until(mockCondition));
8691

@@ -94,7 +99,7 @@ public void checksTimeoutAfterConditionSoZeroTimeoutWaitsCanSucceed() {
9499
when(mockCondition.apply(mockDriver)).thenReturn(null);
95100

96101
Wait<WebDriver> wait = new FluentWait<WebDriver>(mockDriver, mockClock, mockSleeper)
97-
.withTimeout(0, TimeUnit.MILLISECONDS);
102+
.withTimeout(0, TimeUnit.MILLISECONDS);
98103
try {
99104
wait.until(mockCondition);
100105
fail();
@@ -108,14 +113,14 @@ public void canIgnoreMultipleExceptions() throws InterruptedException {
108113
when(mockClock.laterBy(0L)).thenReturn(2L);
109114
when(mockClock.isNowBefore(2L)).thenReturn(true);
110115
when(mockCondition.apply(mockDriver))
111-
.thenThrow(new NoSuchElementException(""))
112-
.thenThrow(new NoSuchFrameException(""))
113-
.thenReturn(ARBITRARY_VALUE);
116+
.thenThrow(new NoSuchElementException(""))
117+
.thenThrow(new NoSuchFrameException(""))
118+
.thenReturn(ARBITRARY_VALUE);
114119

115120
Wait<WebDriver> wait = new FluentWait<WebDriver>(mockDriver, mockClock, mockSleeper)
116-
.withTimeout(0, TimeUnit.MILLISECONDS)
117-
.pollingEvery(2, TimeUnit.SECONDS)
118-
.ignoring(NoSuchElementException.class, NoSuchFrameException.class);
121+
.withTimeout(0, TimeUnit.MILLISECONDS)
122+
.pollingEvery(2, TimeUnit.SECONDS)
123+
.ignoring(NoSuchElementException.class, NoSuchFrameException.class);
119124

120125
assertEquals(ARBITRARY_VALUE, wait.until(mockCondition));
121126

@@ -130,9 +135,9 @@ public void propagatesUnIgnoredExceptions() {
130135
when(mockCondition.apply(mockDriver)).thenThrow(exception);
131136

132137
Wait<WebDriver> wait = new FluentWait<WebDriver>(mockDriver, mockClock, mockSleeper)
133-
.withTimeout(0, TimeUnit.MILLISECONDS)
134-
.pollingEvery(2, TimeUnit.SECONDS)
135-
.ignoring(NoSuchElementException.class, NoSuchFrameException.class);
138+
.withTimeout(0, TimeUnit.MILLISECONDS)
139+
.pollingEvery(2, TimeUnit.SECONDS)
140+
.ignoring(NoSuchElementException.class, NoSuchFrameException.class);
136141

137142
try {
138143
wait.until(mockCondition);
@@ -148,14 +153,14 @@ public void timeoutMessageIncludesLastIgnoredException() {
148153

149154
when(mockClock.laterBy(0L)).thenReturn(2L);
150155
when(mockCondition.apply(mockDriver))
151-
.thenThrow(exception)
152-
.thenReturn(null);
156+
.thenThrow(exception)
157+
.thenReturn(null);
153158
when(mockClock.isNowBefore(2L)).thenReturn(false);
154159

155160
Wait<WebDriver> wait = new FluentWait<WebDriver>(mockDriver, mockClock, mockSleeper)
156-
.withTimeout(0, TimeUnit.MILLISECONDS)
157-
.pollingEvery(2, TimeUnit.SECONDS)
158-
.ignoring(NoSuchWindowException.class);
161+
.withTimeout(0, TimeUnit.MILLISECONDS)
162+
.pollingEvery(2, TimeUnit.SECONDS)
163+
.ignoring(NoSuchWindowException.class);
159164
try {
160165
wait.until(mockCondition);
161166
fail();
@@ -167,15 +172,45 @@ public void timeoutMessageIncludesLastIgnoredException() {
167172
@Test
168173
public void timeoutMessageIncludesCustomMessage() {
169174
TimeoutException expected = new TimeoutException(
170-
"Timed out after 0 seconds: Expected custom timeout message");
175+
"Timed out after 0 seconds: Expected custom timeout message");
171176

172177
when(mockClock.laterBy(0L)).thenReturn(2L);
173178
when(mockCondition.apply(mockDriver)).thenReturn(null);
174179
when(mockClock.isNowBefore(2L)).thenReturn(false);
175180

176181
Wait<WebDriver> wait = new FluentWait<WebDriver>(mockDriver, mockClock, mockSleeper)
177-
.withTimeout(0, TimeUnit.MILLISECONDS)
178-
.withMessage("Expected custom timeout message");
182+
.withTimeout(0, TimeUnit.MILLISECONDS)
183+
.withMessage("Expected custom timeout message");
184+
185+
try {
186+
wait.until(mockCondition);
187+
fail();
188+
} catch (TimeoutException actual) {
189+
assertEquals(expected.getMessage(), actual.getMessage());
190+
}
191+
}
192+
193+
private String state = null;
194+
195+
@Test
196+
public void timeoutMessageIncludesCustomMessageEvaluatedOnFailure() {
197+
TimeoutException expected = new TimeoutException(
198+
"Timed out after 0 seconds: external state");
199+
200+
when(mockClock.laterBy(0L)).thenReturn(2L);
201+
when(mockCondition.apply(mockDriver)).thenReturn(null);
202+
when(mockClock.isNowBefore(2L)).thenReturn(false);
203+
204+
Wait<WebDriver> wait = new FluentWait<WebDriver>(mockDriver, mockClock, mockSleeper)
205+
.withTimeout(0, TimeUnit.MILLISECONDS)
206+
.withMessage(new Supplier<String>() {
207+
@Override
208+
public String get() {
209+
return state;
210+
}
211+
});
212+
213+
state = "external state";
179214

180215
try {
181216
wait.until(mockCondition);
@@ -188,7 +223,7 @@ public void timeoutMessageIncludesCustomMessage() {
188223
@Test
189224
public void timeoutMessageIncludesToStringOfCondition() {
190225
TimeoutException expected = new TimeoutException(
191-
"Timed out after 0 seconds waiting for toString called");
226+
"Timed out after 0 seconds waiting for toString called");
192227

193228
Function<Object, Boolean> condition = new Function<Object, Boolean>() {
194229
public Boolean apply(Object ignored) {
@@ -202,7 +237,7 @@ public String toString() {
202237
};
203238

204239
Wait<Object> wait = new FluentWait<Object>("cheese")
205-
.withTimeout(0, TimeUnit.MILLISECONDS);
240+
.withTimeout(0, TimeUnit.MILLISECONDS);
206241

207242
try {
208243
wait.until(condition);
@@ -221,9 +256,9 @@ public void canIgnoreThrowables() {
221256
when(mockClock.isNowBefore(2L)).thenReturn(false);
222257

223258
Wait<WebDriver> wait = new FluentWait<WebDriver>(mockDriver, mockClock, mockSleeper)
224-
.withTimeout(0, TimeUnit.MILLISECONDS)
225-
.pollingEvery(2, TimeUnit.SECONDS)
226-
.ignoring(AssertionError.class);
259+
.withTimeout(0, TimeUnit.MILLISECONDS)
260+
.pollingEvery(2, TimeUnit.SECONDS)
261+
.ignoring(AssertionError.class);
227262

228263
try {
229264
wait.until(mockCondition);
@@ -248,8 +283,8 @@ protected RuntimeException timeoutException(String message, Throwable lastExcept
248283
}
249284
};
250285
wait.withTimeout(0, TimeUnit.MILLISECONDS)
251-
.pollingEvery(2, TimeUnit.SECONDS)
252-
.ignoring(TimeoutException.class);
286+
.pollingEvery(2, TimeUnit.SECONDS)
287+
.ignoring(TimeoutException.class);
253288

254289
try {
255290
wait.until(mockCondition);
@@ -259,8 +294,11 @@ protected RuntimeException timeoutException(String message, Throwable lastExcept
259294
}
260295
}
261296

262-
private static class TestException extends RuntimeException {}
297+
private static class TestException extends RuntimeException {
298+
299+
}
263300

264301
public interface GenericCondition extends ExpectedCondition<Object> {
302+
265303
}
266304
}

0 commit comments

Comments
 (0)