11using System ;
22using System . Threading . Tasks ;
33using PuppeteerSharp . Tests . Attributes ;
4+ using PuppeteerSharp . Transport ;
45using PuppeteerSharp . Xunit ;
56using Xunit ;
67using Xunit . Abstractions ;
78
89namespace PuppeteerSharp . Tests . WaitTaskTests
910{
1011 [ Collection ( TestConstants . TestFixtureCollectionName ) ]
11- public class FrameWaitForFunctionTests : PuppeteerPageBaseTest
12+ public sealed class FrameWaitForFunctionTests : PuppeteerPageBaseTest , IDisposable
1213 {
14+ private ConnectionTransportInterceptor _connectionTransportInterceptor ;
15+
1316 public FrameWaitForFunctionTests ( ITestOutputHelper output ) : base ( output )
1417 {
18+ DefaultOptions = TestConstants . DefaultBrowserOptions ( ) ;
19+
20+ // Set up a custom TransportFactory to intercept sent messages
21+ // Some of the tests require making assertions after a WaitForFunction has
22+ // started, but before it has resolved. We detect that reliably by
23+ // listening to the message that is sent to start polling.
24+ // This might not be an issue in upstream puppeteer.js, or may be highly unlikely,
25+ // due to differences between node.js's task scheduler and .net's.
26+ DefaultOptions . TransportFactory = async ( url , options , cancellationToken ) =>
27+ {
28+ _connectionTransportInterceptor = new ConnectionTransportInterceptor ( await WebSocketTransport . DefaultTransportFactory ( url , options , cancellationToken ) ) ;
29+ return _connectionTransportInterceptor ;
30+ } ;
31+ }
32+
33+ public void Dispose ( )
34+ {
35+ _connectionTransportInterceptor . Dispose ( ) ;
1536 }
1637
1738 [ PuppeteerTest ( "waittask.spec.ts" , "Frame.waitForFunction" , "should work when resolved right before execution context disposal" ) ]
@@ -33,26 +54,24 @@ public async Task ShouldPollOnInterval()
3354 {
3455 var startTime = DateTime . UtcNow ;
3556 var polling = 100 ;
57+ var startedPolling = WaitForStartPollingAsync ( ) ;
3658 var watchdog = Page . WaitForFunctionAsync ( "() => window.__FOO === 'hit'" , new WaitForFunctionOptions { PollingInterval = polling } ) ;
37- // Wait for function will release the execution faster than in node.
38- // We add some CDP action to wait for the task to start the polling
39- await Page . EvaluateExpressionAsync ( "document.body.appendChild(document.createElement('div'))" ) ;
59+ await startedPolling ;
4060 await Page . EvaluateFunctionAsync ( "() => setTimeout(window.__FOO = 'hit', 50)" ) ;
4161 await watchdog ;
4262
4363 Assert . True ( ( DateTime . UtcNow - startTime ) . TotalMilliseconds > polling / 2 ) ;
4464 }
45-
65+
4666 [ PuppeteerTest ( "waittask.spec.ts" , "Frame.waitForFunction" , "should poll on interval async" ) ]
4767 [ PuppeteerFact ]
4868 public async Task ShouldPollOnIntervalAsync ( )
4969 {
5070 var startTime = DateTime . UtcNow ;
5171 var polling = 1000 ;
72+ var startedPolling = WaitForStartPollingAsync ( ) ;
5273 var watchdog = Page . WaitForFunctionAsync ( "async () => window.__FOO === 'hit'" , new WaitForFunctionOptions { PollingInterval = polling } ) ;
53- // Wait for function will release the execution faster than in node.
54- // We add some CDP action to wait for the task to start the polling
55- await Page . EvaluateExpressionAsync ( "document.body.appendChild(document.createElement('div'))" ) ;
74+ await startedPolling ;
5675 await Page . EvaluateFunctionAsync ( "async () => setTimeout(window.__FOO = 'hit', 50)" ) ;
5776 await watchdog ;
5877 Assert . True ( ( DateTime . UtcNow - startTime ) . TotalMilliseconds > polling / 2 ) ;
@@ -63,9 +82,11 @@ public async Task ShouldPollOnIntervalAsync()
6382 public async Task ShouldPollOnMutation ( )
6483 {
6584 var success = false ;
85+ var startedPolling = WaitForStartPollingAsync ( ) ;
6686 var watchdog = Page . WaitForFunctionAsync ( "() => window.__FOO === 'hit'" ,
6787 new WaitForFunctionOptions { Polling = WaitForFunctionPollingOption . Mutation } )
6888 . ContinueWith ( _ => success = true ) ;
89+ await startedPolling ;
6990 await Page . EvaluateExpressionAsync ( "window.__FOO = 'hit'" ) ;
7091 Assert . False ( success ) ;
7192 await Page . EvaluateExpressionAsync ( "document.body.appendChild(document.createElement('div'))" ) ;
@@ -77,9 +98,11 @@ public async Task ShouldPollOnMutation()
7798 public async Task ShouldPollOnMutationAsync ( )
7899 {
79100 var success = false ;
101+ var startedPolling = WaitForStartPollingAsync ( ) ;
80102 var watchdog = Page . WaitForFunctionAsync ( "async () => window.__FOO === 'hit'" ,
81103 new WaitForFunctionOptions { Polling = WaitForFunctionPollingOption . Mutation } )
82104 . ContinueWith ( _ => success = true ) ;
105+ await startedPolling ;
83106 await Page . EvaluateFunctionAsync ( "async () => window.__FOO = 'hit'" ) ;
84107 Assert . False ( success ) ;
85108 await Page . EvaluateExpressionAsync ( "document.body.appendChild(document.createElement('div'))" ) ;
@@ -216,5 +239,22 @@ public async Task ShouldSurviveNavigations()
216239 await Page . EvaluateFunctionAsync ( "() => window.__done = true" ) ;
217240 await watchdog ;
218241 }
242+
243+ private Task < bool > WaitForStartPollingAsync ( )
244+ {
245+ TaskCompletionSource < bool > startedPolling = new TaskCompletionSource < bool > ( ) ;
246+
247+ // Wait for function will release the execution faster than in node.
248+ // We intercept the poller.start() call to prevent tests from continuing before the polling has started.
249+ _connectionTransportInterceptor . MessageSent += ( _ , message ) =>
250+ {
251+ if ( message . Contains ( "poller => poller.start()" ) )
252+ {
253+ startedPolling . SetResult ( true ) ;
254+ }
255+ } ;
256+
257+ return startedPolling . Task ;
258+ }
219259 }
220- }
260+ }
0 commit comments