8
8
using System . Diagnostics ;
9
9
using System . Linq ;
10
10
using System . Threading ;
11
+ using System . Threading . Tasks ;
11
12
12
13
namespace Aquality . Selenium . Core . Waitings
13
14
{
@@ -79,6 +80,44 @@ public bool WaitFor(Func<bool> condition, TimeSpan? timeout = null, TimeSpan? po
79
80
} , new List < Type > { typeof ( TimeoutException ) } ) ;
80
81
}
81
82
83
+ /// <summary>
84
+ /// Wait for some condition asynchronously within timeout.
85
+ /// </summary>
86
+ /// <param name="condition">Predicate for waiting</param>
87
+ /// <param name="timeout">Condition timeout. Default value is <see cref="ITimeoutConfiguration.Condition"/></param>
88
+ /// <param name="pollingInterval">Condition check interval. Default value is <see cref="ITimeoutConfiguration.PollingInterval"/></param>
89
+ /// <param name="exceptionsToIgnore">Possible exceptions that have to be ignored. </param>
90
+ /// <returns>A task that returns true if condition satisfied and false otherwise.</returns>
91
+ public Task < bool > WaitForAsync ( Func < bool > condition , TimeSpan ? timeout = null , TimeSpan ? pollingInterval = null , IList < Type > exceptionsToIgnore = null )
92
+ {
93
+ if ( condition == null )
94
+ {
95
+ throw new ArgumentNullException ( nameof ( condition ) , "condition cannot be null" ) ;
96
+ }
97
+ var waitTimeout = ResolveConditionTimeout ( timeout ) ;
98
+ var checkInterval = ResolvePollingInterval ( pollingInterval ) ;
99
+ return WaitForAsyncCore ( condition , exceptionsToIgnore , waitTimeout , checkInterval ) ;
100
+ }
101
+
102
+ /// <summary>
103
+ /// Wait for some condition asynchronously within timeout.
104
+ /// </summary>
105
+ /// <param name="condition">Predicate for waiting</param>
106
+ /// <param name="timeout">Condition timeout. Default value is <see cref="ITimeoutConfiguration.Condition"/></param>
107
+ /// <param name="pollingInterval">Condition check interval. Default value is <see cref="ITimeoutConfiguration.PollingInterval"/></param>
108
+ /// <param name="message">Part of error message in case of Timeout exception</param>
109
+ /// <param name="exceptionsToIgnore">Possible exceptions that have to be ignored. </param>
110
+ /// <exception cref="TimeoutException">Throws when timeout exceeded and condition not satisfied, if only the task is awaited.</exception>
111
+ /// <returns>A task that throws a <see cref="TimeoutException"/> if condition is not satisfied after the timeout.</returns>
112
+ public async Task WaitForTrueAsync ( Func < bool > condition , TimeSpan ? timeout = null , TimeSpan ? pollingInterval = null , string message = null , IList < Type > exceptionsToIgnore = null )
113
+ {
114
+ var waitTimeout = ResolveConditionTimeout ( timeout ) ;
115
+ if ( ! await WaitForAsync ( condition , waitTimeout , pollingInterval , exceptionsToIgnore ) )
116
+ {
117
+ throw GetTimeoutException ( waitTimeout , message ) ;
118
+ }
119
+ }
120
+
82
121
/// <summary>
83
122
/// Wait for some condition within timeout.
84
123
/// </summary>
@@ -107,19 +146,24 @@ public void WaitForTrue(Func<bool> condition, TimeSpan? timeout = null, TimeSpan
107
146
108
147
if ( stopwatch . Elapsed > waitTimeout )
109
148
{
110
- var exceptionMessage = $ "Timed out after { waitTimeout . TotalSeconds } seconds";
111
- if ( ! string . IsNullOrEmpty ( message ) )
112
- {
113
- exceptionMessage += $ ": { message } ";
114
- }
115
-
116
- throw new TimeoutException ( exceptionMessage ) ;
149
+ throw GetTimeoutException ( waitTimeout , message ) ;
117
150
}
118
151
119
152
Thread . Sleep ( checkInterval ) ;
120
153
}
121
154
}
122
155
156
+ private TimeoutException GetTimeoutException ( TimeSpan waitTimeout , string message )
157
+ {
158
+ var exceptionMessage = $ "Timed out after { waitTimeout . TotalSeconds } seconds";
159
+ if ( ! string . IsNullOrEmpty ( message ) )
160
+ {
161
+ exceptionMessage += $ ": { message } ";
162
+ }
163
+
164
+ return new TimeoutException ( exceptionMessage ) ;
165
+ }
166
+
123
167
private bool IsConditionSatisfied ( Func < bool > condition , IList < Type > exceptionsToIgnore )
124
168
{
125
169
try
@@ -146,5 +190,29 @@ private TimeSpan ResolvePollingInterval(TimeSpan? pollingInterval)
146
190
{
147
191
return pollingInterval ?? timeoutConfiguration . PollingInterval ;
148
192
}
193
+
194
+ private async Task < bool > WaitForAsyncCore ( Func < bool > condition , IList < Type > exceptionsToIgnore , TimeSpan waitTimeout , TimeSpan checkInterval )
195
+ {
196
+ using ( CancellationTokenSource cts = new CancellationTokenSource ( ) )
197
+ {
198
+ var token = cts . Token ;
199
+ var waitTask = Task . Run ( async ( ) =>
200
+ {
201
+ while ( ! IsConditionSatisfied ( condition , exceptionsToIgnore ?? new List < Type > ( ) ) )
202
+ {
203
+ await Task . Delay ( checkInterval ) ;
204
+ }
205
+ } ,
206
+ token ) ;
207
+ var finishedTask = await Task . WhenAny ( waitTask , Task . Delay ( waitTimeout , token ) ) ;
208
+ cts . Cancel ( ) ;
209
+ var result = finishedTask == waitTask ;
210
+ if ( result && waitTask . Exception != null )
211
+ {
212
+ throw waitTask . Exception . InnerExceptions . Count == 1 ? waitTask . Exception . InnerException : waitTask . Exception ;
213
+ }
214
+ return result ;
215
+ }
216
+ }
149
217
}
150
218
}
0 commit comments