playwright-dotnet icon indicating copy to clipboard operation
playwright-dotnet copied to clipboard

[Feature] Allow async predicates for RunAndWaitFor methods

Open maw136 opened this issue 1 year ago • 11 comments

Given RequestFinished events already contain Response object it would be more flexible for the Predicate<T> that is passed by options of RunAndWaitForRequestFinished (and WaitForRequestFinished) methods to use the IReponse object instead of IRequest. I can make the changes in code-base but I'd like to discuss architecture here - before committing to these changes.

maw136 avatar May 12 '23 00:05 maw136

I have created a PR for this: https://github.com/microsoft/playwright-dotnet/pull/2577 It is a code breaking change so I strongly request comments on the issue.

maw136 avatar May 13 '23 01:05 maw136

We strongly try to avoid breaking changes.

Would calling await request.ResponseAsync() work for you to get the response?

Could you share code which demonstrates the issue you are trying to solve?

mxschmitt avatar May 14 '23 14:05 mxschmitt

Let's assume this kind of snippet:

 PageRunAndWaitForRequestFinishedOptions options = new()
            {
                Predicate = finishedRequest => responseTemplate.Matches(finishedRequest.Url, new HttpMethod(finishedRequest.Method)),
                Timeout = (float?)timeout?.TotalMilliseconds
            };

The Predicate and similar Request/Response matchers assume synchronous context so there is no proper way to use async inside them. What's more, RequestFinished event always happens after Response so, there should always be a valid Response there, or am I missing something?

maw136 avatar May 14 '23 23:05 maw136

The more high level question which I wanted to ask was, what data from the Response object do you try to access in PageRunAndWaitForRequestFinished? In this example its just Request.Url and Request.Method which seems already working fine for your use-case?

mxschmitt avatar May 15 '23 07:05 mxschmitt

I haven't been precise enough. We need to additionally access StatusCode in the Predicate.

maw136 avatar May 15 '23 10:05 maw136

So I've created an additional PR for possibly less breaking effect: https://github.com/microsoft/playwright-dotnet/pull/2578

maw136 avatar May 15 '23 23:05 maw136

But I don't like that solution, even for compatibilities sake.

maw136 avatar May 16 '23 00:05 maw136

Instead of coming up with new APIs or PRs, lets first try to understand the actual problem better. Could you please share more information what you are trying to do? For what kind of network requests do you wait for, what triggers it, is it a normal navigation or after some page interaction like click?

Would RunAndWaitForResponseAsync work for you with an additional await response.FinishedAsync() at the end?

Would it be possible to share a minimal reproduction which shows whats not working?

mxschmitt avatar May 17 '23 22:05 mxschmitt

The requests/responses are triggered by a normal application operation. The app is somewhat talkative, and it does some async requests for new data. We would like to have access to IResponse on RequestFinished event because as the docs say that event is fired after the body of Response had been loaded. We use the (RunAnd)WaitFor... APIs to wait for our application to process stuff. It doesn't work sometimes, when Angular has more data to parse and refresh DOM the flow breaks. We are actively working on a better approach, like waiting for Locators and such. But some of the crucial functionality depends on those Request/Response pairs. I switched back our uses to ...ForResponse... from ...ForRequestFinished... as it's been like that before. The repro is so exceedingly difficult to achieve to be honest, even in perfect conditions. There are reproducible problems with producing the errors such as having a breakpoint near the failing line makes the errors go away. We have a usecase to acquire multiple IResponse objects after Wait. An API to ...WaitForMultiple... would be the best for us. I could introduce such API with your guidance. To be honest I prefer making lots of PRs to see 'what sticks', but I understand that it may not be a good flow for everyone, what I mean is, I explain myself (and our team needs) in code/PR better than in just a natural language text. The other issue with ...RequestFinished... is that the filtering predicate is not async co it can't access the Response of the Request, even that it is finished, but that is strictly connected to the issue of waiting as long as possible as a patch to the race condition that happens. We are waiting for reponses/parsing of responses hoping that the Angular will be fast enough with DOM update. Either way IMHO Response object should be accessible at RequestFinished predicate despite our app. As for click interaction it is also in our flow. Some Clicks trigger multiple responses that we would like to aquire body of - after the predicate filters them out.

I hope it is intelligible. Awaiting all the questions and suggestions. I'd like to contribute good code :)

maw136 avatar May 29 '23 16:05 maw136

Sorry for the late response, was out of office.

We are actively working on a better approach, like waiting for Locators and such. But some of the crucial functionality depends on those Request/Response pairs.

This sounds like web-first assertions should just work for you.

is that the filtering predicate is not async co it can't access the Response of the Request, even that it is finished

I was looking at Node.js, and how we do it there. Turns out, there we have async predicate support.

So we would be more than happy to make the predicate async as well in the RunAndWaitFor methods, e.g. here.

Are you fine if I change this issue title accordingly to that?

mxschmitt avatar Jun 17 '23 18:06 mxschmitt

Of course. Be my guest.

maw136 avatar Jun 19 '23 22:06 maw136