Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[Feature] Add Expect.Poll #2314

Open
mxschmitt opened this issue Sep 14, 2022 · 5 comments
Open

[Feature] Add Expect.Poll #2314

mxschmitt opened this issue Sep 14, 2022 · 5 comments

Comments

@mxschmitt
Copy link
Member

See here: https://playwright.dev/docs/test-assertions#polling

@Exoow
Copy link
Contributor

Exoow commented Jan 16, 2024

Would the suggestion below be a good starting point?
The "problem" (as my simple mind sees the solution) is that you don't need to inherit from AssertionsBase for anything (as you will just retry calling the function parameter - but this prevents knowing the default timeout set in AssertionsBase. Unless it's okay to set a separate default timeout for polling.

FYI my current solution is further below. If a similar thing is good enough (but including backoff intervals and a toBe method) I could give this a shot.

public static class Assertions
{
    // new line, pseudocode-ish:
    public static IPollAssertions Expect<T>(Func<Task<T>> function) => new PollAssertions(function);

    public static ILocatorAssertions Expect(ILocator locator) => new LocatorAssertions(locator, false);
    public static IPageAssertions Expect(IPage page) => new PageAssertions(page, false);
    public static IAPIResponseAssertions Expect(IAPIResponse response) => new APIResponseAssertions(response, false);
}

My current solution:

public static async Task ShouldBeEqual(this Assert assert, string expected, Func<Task<string?>> function)
{
    // Retry method is a do-while loop with Thread.Sleep for the polling intervals, throwing an exception on failure
    await retrier.Retry(async () => Assert.AreEqual(expected, await function()));
}

@mxschmitt
Copy link
Member Author

Would the suggestion below be a good starting point?

The "problem" (as my simple mind sees the solution) is that you don't need to inherit from AssertionsBase for anything (as you will just retry calling the function parameter - but this prevents knowing the default timeout set in AssertionsBase. Unless it's okay to set a separate default timeout for polling.

FYI my current solution is further below. If a similar thing is good enough (but including backoff intervals and a toBe method) I could give this a shot.


public static class Assertions

{

    // new line, pseudocode-ish:

    public static IPollAssertions Expect<T>(Func<Task<T>> function) => new PollAssertions(function);



    public static ILocatorAssertions Expect(ILocator locator) => new LocatorAssertions(locator, false);

    public static IPageAssertions Expect(IPage page) => new PageAssertions(page, false);

    public static IAPIResponseAssertions Expect(IAPIResponse response) => new APIResponseAssertions(response, false);

}

My current solution:


public static async Task ShouldBeEqual(this Assert assert, string expected, Func<Task<string?>> function)

{

    // Retry method is a do-while loop with Thread.Sleep for the polling intervals, throwing an exception on failure

    await retrier.Retry(async () => Assert.AreEqual(expected, await function()));

}

I would recommend Task.Delay instead, otherwise it looks reasonable!

Exoow pushed a commit to Exoow/playwright-dotnet that referenced this issue Jan 24, 2024
@Exoow
Copy link
Contributor

Exoow commented Jan 24, 2024

@mxschmitt could you have a look at the linked commit please? Is this usable?
Also, do I need to do anything with the "API generation" tool mentioned in the Contributing? I manually added interfaces to the Generated folder in the project.

@mxschmitt
Copy link
Member Author

Looks great! But more like Expect.ToPass rather than Poll. I think we could reshape it a bit, so it matches with the JavaScript API for ToPass and then close the Expect.Poll feature request:

        await Expect(async () =>
        {
            var foo = await Page.EvaluateAsync<string>("() => document.title");
            Assert.AreEqual("Hello world", foo);
        }).ToPassAsync(new () {
            Intervals = new[] { 1_000, 2_000, 10_000 },
            Timeout = 60_000,
        });

Something like this I was envisioning.

If we want to implement expect.Poll, it should look like this:

await Expect.Poll(async () => {
  var response = await Page.APIRequest.GetAsync("https://api.example.com");
  return response.Status;
}, new() {
  // Custom error message, optional.
  message = "make sure API eventually succeeds", // custom error message
  // Poll for 10 seconds; defaults to 5 seconds. Pass 0 to disable timeout.
  timeout: 10000,
}).toBeAsync(200);

Which is much more effort to implement, since we don't have the "default expect matchers like toBe" in .NET compared to Node.js where we get them from the expect Library. In .NET we have Assert for that. So this seems out of scope and ToPass like you did seems like a great feature!

@Exoow
Copy link
Contributor

Exoow commented Jan 30, 2024

@mxschmitt Okay, I understand and will adapt. I'll also update the logic so it ignores any exceptions until success or timeout.
Thanks for the feedback so far!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

2 participants