88using System . Diagnostics ;
99using System . Linq ;
1010using System . Threading ;
11+ using System . Threading . Tasks ;
1112
1213namespace Aquality . Selenium . Core . Waitings
1314{
@@ -79,6 +80,44 @@ public bool WaitFor(Func<bool> condition, TimeSpan? timeout = null, TimeSpan? po
7980 } , new List < Type > { typeof ( TimeoutException ) } ) ;
8081 }
8182
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+
82121 /// <summary>
83122 /// Wait for some condition within timeout.
84123 /// </summary>
@@ -107,19 +146,24 @@ public void WaitForTrue(Func<bool> condition, TimeSpan? timeout = null, TimeSpan
107146
108147 if ( stopwatch . Elapsed > waitTimeout )
109148 {
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 ) ;
117150 }
118151
119152 Thread . Sleep ( checkInterval ) ;
120153 }
121154 }
122155
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+
123167 private bool IsConditionSatisfied ( Func < bool > condition , IList < Type > exceptionsToIgnore )
124168 {
125169 try
@@ -146,5 +190,29 @@ private TimeSpan ResolvePollingInterval(TimeSpan? pollingInterval)
146190 {
147191 return pollingInterval ?? timeoutConfiguration . PollingInterval ;
148192 }
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+ }
149217 }
150218}
0 commit comments