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

Pausing and resuming intercepted requests on demand #16472

Closed
ezhikov opened this issue May 12, 2021 · 9 comments
Closed

Pausing and resuming intercepted requests on demand #16472

ezhikov opened this issue May 12, 2021 · 9 comments
Labels
stage: awaiting response Potential fix was proposed; awaiting response stale no activity on this issue for a long period topic: cy.intercept()

Comments

@ezhikov
Copy link

ezhikov commented May 12, 2021

What would you like?

I'd like ability to pause intercepted request indefinitely and then resume it at will when I need with response which I need.

Why is this needed?

I'm using @xstate/test for testing. It generates scenarios based on states and events with different paths. Problem I encounter is that when scenario includes race condition, I can't guarantee order of execution inside application. For example, I have two requests: A and B. In one scenario A should settle first, and in another B should settle first.

Approaches I tried to solve this problem:

  • Inspect each scenario beforehand and add delay to every interception, according to their precedence. This works, but only with pretty significant delays (more than a second). This leads to very long time of execution.
  • Return promise within intercept callback and resolve this promise outside. For some reason this didn't work as expected at all - requests are still passing through.

Another issue that I have is that there can be multiple responses for request, depending on scenario. Right now I have to inspect each scenario beforehand and stub responses. I'd like to do it on demand when I need to settle response.

If we abstract from model based testing, I'd like to write something like this:

beforeEach(function() {
  // intercept request and pause it until resolved
  cy.intercept('/api/a').as('a');
  cy.intercept('/api/b').as('b');
  cy.visit('http://localhost:3000');
});

it('should resolve A first', () => {
  // assert loading state
  cy.wait('@a').then(/* resolve somehow with specific response */);
  // assert `a` result and `b` loading state
  cy.wait('@b').then(/* resolve somehow with specific response */);
  // assert `b` result
})

it('should resolve B first', () => {
  // assert loading state
  cy.wait('@b').then(/* resolve somehow with specific response */);
  // assert `b` result and `a` loading state
  cy.wait('@b').then(/* resolve somehow with specific response */);
  // assert `a` result
})
@jennifer-shehane
Copy link
Member

Have you tried passing an array of aliases to cy.wait()? This way it would wait for all requests, regardless of order.

cy.intercept('/api/a').as('a');
cy.intercept('/api/b').as('b');
cy.visit('http://localhost:3000');

cy.wait(['@a', '@b']).then(
  (interceptions) => {
    // interceptions will now be an array of matching requests
    // interceptions[0] <-- a
    // interceptions[1] <-- b
  }
)

The response resolution will always be defined in the cy.intercept() definition ahead of time. The .then() on the .wait() is just to inspect the actual request and response at that point. https://on.cypress.io/intercept#Controlling-the-response

cy.intercept('/api/a', (req) => {
  // do something with the intercepted request
})

@jennifer-shehane jennifer-shehane added stage: awaiting response Potential fix was proposed; awaiting response topic: cy.intercept() labels May 12, 2021
@ezhikov
Copy link
Author

ezhikov commented May 13, 2021

This way it would wait for all requests, regardless of order.

With two requests I have two possible orders of resolution and I want it to be deterministic for each test case.

The response resolution will always be defined in the cy.intercept() definition ahead of time.

This is my main point. I now have to:

  • Stub everything with default responses
  • Stub case routes with preferred responses
  • Orchestrate delays, so responses would resolve in particular order
  • Wait patiently for a long time while tests are running (because of delays)

Having ability to resolve requests deterministically as test case goes without need to juggle delays would be wonderful

EDIT:

I forgot to add another benefit of stopping everything and resolving by case basis on demand. If I have multiple requests for the same route with different outcomes. Let's say I'm fetching user profile. To properly test, I need to respond with error and then retry either with error again, or with data.

@flotwig
Copy link
Contributor

flotwig commented May 13, 2021

If I have multiple requests for the same route with different outcomes. Let's say I'm fetching user profile. To properly test, I need to respond with error and then retry either with error again, or with data.

You may be interested in the times property on RouteMatcher, which came out in 7.3.0: #8531 With { times: 1 }, you could simulate such an intermittent fault.

@jennifer-shehane
Copy link
Member

@ezhikov It may be helpful to see the solutions with cy.intercept() that you already tried and the errors that you're encountering rather than the proposed solution upfront. There might be an existing workaround, just need some more clarity on your currently written test structure.

@flotwig
Copy link
Contributor

flotwig commented May 13, 2021

^ yeah, your second proposal -

Return promise within intercept callback and resolve this promise outside. For some reason this didn't work as expected at all - requests are still passing through.

- is exactly how I'd recommend solving this problem, so if it's not working, there might be some bug/something we can document better.

@ezhikov
Copy link
Author

ezhikov commented May 14, 2021

It may be helpful to see the solutions with cy.intercept() that you already tried and the errors that you're encountering rather than the proposed solution upfront.

there might be some bug/something we can document better

I'll make suitable demo to illustrate what I want to achieve this weekend

@m-janicki
Copy link

m-janicki commented Oct 3, 2021

Hi, I think I've faced a very similar issue. My app is allowing user to ask for data with some custom filter, the query is passed via http request. The user can ask multiple times in a row for data with different filters, but only the most recent query is meaningful. So with scenario below

  1. User asks for data with filter A
  2. User asks for data with filter B
  3. Server responds with data filtered by B (dataB)
  4. Server responds with data filtered by A (dataA)

App is supposed to ignore the 4th point (as opposed to loading the dataA into the app).

I couldn't find a way to force the order of responses, other than adding a delay. Something like

  it("handles delayed, obsolete responses", () => {
    cy.intercept("/api/data?filter=example-A", {
      delay: 700,
      body: dataA
    }).as("delayed");

    cy.intercept("/api/data?filter=example-B", dataB);

    cy.visit('http://localhost:3000');

    cy.get("[data-cy=filter]").type("example-A{enter}");
    cy.get("[data-cy=filter]").type("example-B{enter}");

    cy.wait("@delayed");
    // now assert if app body matches dataB
  });

Now this solution is a bit sketchy - it is slow, and doesn't really guarantee the order of responses.

@cypress-app-bot
Copy link
Collaborator

This issue has not had any activity in 180 days. Cypress evolves quickly and the reported behavior should be tested on the latest version of Cypress to verify the behavior is still occurring. It will be closed in 14 days if no updates are provided.

@cypress-app-bot cypress-app-bot added the stale no activity on this issue for a long period label May 17, 2023
@cypress-app-bot
Copy link
Collaborator

This issue has been closed due to inactivity.

@cypress-app-bot cypress-app-bot closed this as not planned Won't fix, can't repro, duplicate, stale May 31, 2023
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
stage: awaiting response Potential fix was proposed; awaiting response stale no activity on this issue for a long period topic: cy.intercept()
Projects
None yet
Development

No branches or pull requests

5 participants