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

feat: Track and allow querying in-flight requests #186

Conversation

tehhowch
Copy link
Contributor

@tehhowch tehhowch commented Dec 4, 2021

  • Start tracking a request as soon as it is issued, rather than only after it has completed
  • Lay groundwork for future waitForPendingRequests option from Feature Request: allow querying for in-progress HTTP calls #111
    • consumers can write their own version now by wrapping the new browser.hasPendingRequests() command
  • deprecates the boolean param to assertExpectedRequestsOnly
  • more tests!

I took care to preserve the current behavior of the library w.r.t getRequest / getRequests ordering, which (despite the previous README's statement) always reported only completed requests, and always ordered by the time of completion. With the new functionality, the next major version of this library could change the default ordering to time of initiation, and could default to waiting for pending requests to settle, etc, -- which are probably the expected behavior for someone new to this library.

Closes #111
Ref #149 #140

 - update middleware to support delayed responses
 - `addRequest` always sets `pending: false`
 - while `typeof null === 'object'`, clarify that body and response.body can be null.
 - if not passing options object (all past code), will continue to receive completed requests only
 - if passing options object with includePending = false, will receive completed requests only
 - if passing options object with includePending = true, will receive all requests.
 - refactor tests for pending requests into own describe block, and add explicit waits to avoid affecting following tests.
…tion

Support function overloads in future work
…ract method for getting contents

 - add runtime check for incomplete polyfill scenario, where fetch is polyfilled but Promise.all is not.
…uests

& parse the response body only if marked as fulfilled
 - in sync mode, these were awaited implicitly. In async mode, however, they must be explicitly awaited.
 - Increase assertion strictness on the matched errors
   - due to an issue with how assert.rejects forwards a regexp containing an escaped regexp, use a validation function to directly invoke assert.matches with the regexp
 - add requests prior to invoking window._fetch
 - complete requests after the full response body is available
 - note: must stringify `lastURL` in case XHR#open is invoked with a URL object.
 - replace `if-else if` chain with `if + return`
 - log the serialization error, if any
consumers should be able to easily identify the source of an issue when they are running tests
Ideally these logs would be passed to the node / test context, rather than emitted in the tested browser, but depending on the test runner they may be surfaced without much additional work
This is the existing behavior as of 4.1.10 and all earlier versions
simplify assignment to `request` in node context
The behavior in 4.1.10 and earlier versions is that requests are ordered by time of fulfillment (`orderBy: 'END'`). This should remain the default behavior, but users should be able to order by the time the request started as well (which is possibly more under their control, in terms of testing, too).
When pending requests are requested, this default sort will place them last (as they are not yet "completed").
… methods

 - deprecate boolean param to assertExpectedRequestsOnly
   - pass `inOrder` as an option instead
 - don't suggest asserting on pending requests will work
   - Since a pending request has no response, it has no response status code. (Passing `includePending: true` would result in a TypeError in the library code).
   - A better approach will be to support an option that requires pending requests are completed before performing assertions.
@tehhowch
Copy link
Contributor Author

tehhowch commented Dec 4, 2021

@chmanie @christian-bromann please take a look!

}
/**
* Convert T to T or Promise<T> depending if `@wdio/sync` is being used or not.
*/
type AsyncSync<T> = WebdriverIO.BrowserObject extends WebDriver.Client
type AsyncSync<T> = WebdriverIO.Browser extends WebDriver.Client
Copy link
Contributor Author

@tehhowch tehhowch Dec 5, 2021

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@christian-bromann I don't believe this configuration is correct -- I had turned on library type checking and it complained that the WebdriverIO namespace didn't have an entry for BrowserObject, but was fine with Browser instead. There is still an error for Client not being found in the Webdriver namespace.

With & without this change to the AsyncSync conditional type, it seems this library's types always evaluate to "sync mode" in a consuming TypeScript project:

image

How might this library (and others that want to provide accurate types for custom commands) best support sync & async modes in WebdriverIO versions 5, 6, and 7 (or at minimum v6 and later, as v6 is still officially a supported version)?

(Existing issues suggest that this is not a new issue with this library, though, e.g. #148 #64 #80 )

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

How might this library (and others that want to provide accurate types for custom commands) best support sync & async modes in WebdriverIO versions 5, 6, and 7 (or at minimum v6 and later, as v6 is still officially a supported version)?

With the help of the community. While it would be amazing to provide best support for sync (with or without typescript) I doubt that we have enough resources to do so. Therefor we are happy if someone helps us with pull requests.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I asked since you are most familiar with the wdio types found in both V6 and V7 and should be most able to tell the community what WDIO interface hierarchy is found in both sync v6 and sync v7 and thus can be used for sync detection in a d.ts file (since we cannot do a require('@wdio/sync') test).

Copy link
Contributor

@christian-bromann christian-bromann left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The change looks generally good to me. Thanks for all the work 👍 👏

Is there a way we can some e2e tests for it?
Given I am not too familiar with the code base it would be great if @chmanie can chime in for an additional review.

@tehhowch
Copy link
Contributor Author

tehhowch commented Dec 6, 2021

Is there a way we can some e2e tests for it?

@christian-bromann This PR does add quite a few E2E tests to that file, would you mind clarifying?

@christian-bromann
Copy link
Contributor

@tehhowch you are right, I was overseeing them because GitHub collapsed them. All good to me them. Let's give @chmanie some time to chime in and if there is no feedback I will go ahead and merge. Thanks so much!

Copy link
Member

@chmanie chmanie left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is amazing work! Thank you so much for putting in the effort, this will be super helpful to a lot of people. Generally this looks good to me, even though I added some comments (we should especially look into the case for a major version bump). Could you also amend the changelog, like so (we're not doing this yet, but it'd be super helpful):

### [ [>](https://github.com/chmanie/wdio-intercept-service/tree/v.next) ] v.next / <DATE>
- Describe changes here (thanks to @tehhowch)

* `request.response.headers`: response http headers as JS object
* `request.response.body`: response body (will be parsed as JSON if possible)
* `request.response.statusCode`: response status code
* `request.pending`: boolean flag for whether this request is complete (i.e. has a `response` property), or in-flight.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

👍

README.md Outdated Show resolved Hide resolved
return getRequest().then((requests) => {

// Don't let users request pending requests:
if (options.includePending) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Very nice catch here, providing a less frustrating experience :)

const expectations = this._wdajaxExpectations;
let inOrder = true;
let options = {};
if (typeof orderOrOptions === 'boolean') {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Honestly, I think I would be OK with changing the API (like in the readme) and bumping the major version

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I vastly prefer the approach of Ember.JS, where the framework's policy is that users should be able to upgrade to the next major versions without their app breaking so as long as their app runs the previous version without encountering deprecation warnings. That is, a semver major bump should never add functionality, it should only remove already-deprecated functionality.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah okay, I see your point. The downside here is that the actual API is not necessarily 100% consistent with the actual code but that might only cause confusion for people who actually work on the code and not for the ones using it. All in all I'd be fine with this.

body: parseBody(req.requestBody),
pending: true,
};
// Check for a '__fulfilled' property on the retrieved request, which is
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nice.

window.sessionStorage.removeItem(NAMESPACE);
}

if (typeof window.fetch == 'function') {
replaceFetch();
if (
typeof window.Promise === 'undefined' ||
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is there ever a case where window.fetch exists, but window.Promise does not? I can only imagine very weird (and broken!) cases of polyfilling.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There shouldn't, but I could imagine that someone might polyfill fetch, Promise.then, and Promise.catch, but not polyfill Promise.all. I figured it was better that we detect this scenario and throw promptly, rather than fail when the fetch wrapper is invoked because of this library's use of Promise.all

lib/interceptor.js Outdated Show resolved Hide resolved
@tehhowch
Copy link
Contributor Author

tehhowch commented Dec 6, 2021

@chmanie updated the changelog (I added a couple of entries for the latest releases too). It may be worth changing to use an automated changelog generator, since that seems to be the common approach for webdriverio projects as well

@chmanie
Copy link
Member

chmanie commented Dec 6, 2021

Good work! All good from my side! Feel free to merge away

@tehhowch
Copy link
Contributor Author

tehhowch commented Dec 7, 2021

@christian-bromann ^

Copy link
Contributor

@christian-bromann christian-bromann left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Awesome! Thanks for the review @chmanie and the work @tehhowch 👍

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

Successfully merging this pull request may close these issues.

Feature Request: allow querying for in-progress HTTP calls
3 participants