Skip to content

Commit

Permalink
Implement Frame.waitForFunction (#157)
Browse files Browse the repository at this point in the history
  • Loading branch information
Meir017 authored and kblok committed Apr 20, 2018
1 parent 5858143 commit 37ef53e
Show file tree
Hide file tree
Showing 4 changed files with 96 additions and 4 deletions.
81 changes: 81 additions & 0 deletions lib/PuppeteerSharp.Tests/Frame/WaitForFunctionTests.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,81 @@
using System;
using System.Threading.Tasks;
using Xunit;

namespace PuppeteerSharp.Tests.Frame
{
[Collection("PuppeteerLoaderFixture collection")]
public class WaitForFunctionTests : PuppeteerPageBaseTest
{
[Fact]
public async Task ShouldPollOnInterval()
{
var success = false;
var startTime = DateTime.Now;
var polling = 100;
var watchdog = Page.WaitForFunctionAsync("() => window.__FOO === 'hit'", new WaitForFunctionOptions { PollingInterval = polling })
.ContinueWith(_ => success = true);
await Page.EvaluateExpressionAsync("window.__FOO = 'hit'");
Assert.False(success);
await Page.EvaluateExpressionAsync("document.body.appendChild(document.createElement('div'))");
await watchdog;
Assert.True((DateTime.Now - startTime).TotalMilliseconds > polling / 2);
}

[Fact]
public async Task ShouldPollOnMutation()
{
var success = false;
var watchdog = Page.WaitForFunctionAsync("() => window.__FOO === 'hit'",
new WaitForFunctionOptions { Polling = WaitForFunctionPollingOption.Mutation })
.ContinueWith(_ => success = true);
await Page.EvaluateExpressionAsync("window.__FOO = 'hit'");
Assert.False(success);
await Page.EvaluateExpressionAsync("document.body.appendChild(document.createElement('div'))");
await watchdog;
}

[Fact]
public async Task ShouldPollOnRaf()
{
var watchdog = Page.WaitForFunctionAsync("() => window.__FOO === 'hit'",
new WaitForFunctionOptions { Polling = WaitForFunctionPollingOption.Raf });
await Page.EvaluateExpressionAsync("window.__FOO = 'hit'");
await watchdog;
}

[Fact]
public async Task ShouldThrowNegativePollingInterval()
{
var exception = await Assert.ThrowsAsync<ArgumentOutOfRangeException>(()
=> Page.WaitForFunctionAsync("() => !!document.body", new WaitForFunctionOptions { PollingInterval = -10 }));

Assert.Contains("Cannot poll with non-positive interval", exception.Message);
}

[Fact]
public async Task ShouldReturnTheSuccessValueAsAJSHandle()
{
Assert.Equal(5, await (await Page.WaitForFunctionAsync("() => 5")).JsonValue<int>());
}

[Fact]
public async Task ShouldReturnTheWindowAsASuccessValue()
{
Assert.NotNull(await Page.WaitForFunctionAsync("() => window"));
}

[Fact]
public async Task ShouldAcceptElementHandleArguments()
{
await Page.SetContentAsync("<div></div>");
var div = await Page.GetElementAsync("div");
var resolved = false;
var waitForFunction = Page.WaitForFunctionAsync("element => !element.parentElement", div)
.ContinueWith(_ => resolved = true);
Assert.False(resolved);
await Page.EvaluateFunctionAsync("element => element.remove()", div);
await waitForFunction;
}
}
}
2 changes: 1 addition & 1 deletion lib/PuppeteerSharp/Frame.cs
Original file line number Diff line number Diff line change
Expand Up @@ -192,7 +192,7 @@ internal void Detach()
internal Task WaitForTimeoutAsync(int milliseconds) => Task.Delay(milliseconds);

internal Task<JSHandle> WaitForFunctionAsync(string script, WaitForFunctionOptions options, params object[] args)
=> new WaitTask(this, script, options.Polling, options.Timeout, args).Task;
=> new WaitTask(this, script, options.Polling, options.PollingInterval, options.Timeout, args).Task;

internal async Task<ElementHandle> WaitForSelectorAsync(string selector, WaitForSelectorOptions options)
{
Expand Down
5 changes: 5 additions & 0 deletions lib/PuppeteerSharp/WaitForFunctionOptions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -16,5 +16,10 @@ public class WaitForFunctionOptions
/// An interval at which the <c>pageFunction</c> is executed. defaults to <see cref="WaitForFunctionPollingOption.Raf"/>
/// </summary>
public WaitForFunctionPollingOption Polling { get; set; } = WaitForFunctionPollingOption.Raf;

/// <summary>
/// An interval at which the <c>pageFunction</c> is executed. If no value is specified will use <see cref="Polling"/>
/// </summary>
public int? PollingInterval { get; set; }
}
}
12 changes: 9 additions & 3 deletions lib/PuppeteerSharp/WaitTask.cs
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ internal class WaitTask
private readonly Frame _frame;
private readonly string _predicateBody;
private readonly WaitForFunctionPollingOption _polling;
private readonly int? _pollingInterval;
private readonly int _timeout;
private readonly object[] _args;
private readonly Task _timeoutTimer;
Expand Down Expand Up @@ -107,24 +108,29 @@ function onTimeout() {
}
}";

internal WaitTask(Frame frame, string predicateBody, WaitForFunctionPollingOption polling, int timeout, object[] args)
internal WaitTask(Frame frame, string predicateBody, WaitForFunctionPollingOption polling, int? pollingInterval, int timeout, object[] args)
{
if (string.IsNullOrEmpty(predicateBody))
{
throw new ArgumentNullException(nameof(predicateBody));
}
if (pollingInterval <= 0)
{
throw new ArgumentOutOfRangeException(nameof(pollingInterval), "Cannot poll with non-positive interval");
}

_frame = frame;
_predicateBody = $"return ( {predicateBody} )(...args)";
_polling = polling;
_pollingInterval = pollingInterval;
_timeout = timeout;
_args = args;

frame.WaitTasks.Add(this);
_taskCompletion = new TaskCompletionSource<JSHandle>();

_cts = new CancellationTokenSource();

_timeoutTimer = System.Threading.Tasks.Task.Delay(timeout, _cts.Token).ContinueWith(_
=> Termiante(new PuppeteerException($"waiting failed: timeout {timeout}ms exceeded")));

Expand All @@ -143,7 +149,7 @@ internal async void Rerun()
try
{
success = await context.EvaluateFunctionHandleAsync(WaitForPredicatePageFunction,
new object[] { _predicateBody, _polling, _timeout }.Concat(_args).ToArray());
new object[] { _predicateBody, _pollingInterval ?? (object)_polling, _timeout }.Concat(_args).ToArray());
}
catch (Exception ex)
{
Expand Down

0 comments on commit 37ef53e

Please sign in to comment.