3

I am trying to end to end test a file upload page in Cypress, which includes testing if a file upload progress bar works.

Unfortunately, in the local environment the upload happens instantly so the progress bar is not shown.

I am aware that you can throttle the response speed using cy.intercept() to simulate slow networks. However, this isn't slowing the request upload speed:

cy.intercept('post', `/route`, (req) => {
    req.on('response', (res) => {
        res.setThrottle(1000) 
    })
}).as('post')

Is there any way that I can apply a throttle to the outgoing request?

Fody
  • 23,754
  • 3
  • 20
  • 37
Rory
  • 2,175
  • 1
  • 27
  • 37
  • What turns the progress bar off? If a response from the server "upload complete" then `res.setThrottle(1000)` will do it. Please provide details. – TesterDick Mar 07 '22 at 21:56

2 Answers2

5

In the routeHandler, use setTimeout() to delay the call to req.continue().

To get the command queue to wait for setTimeout, return a Promise wrapper. See Request phase

If the handler returned a Promise, wait for the Promise to resolve.

If you want a delay longer than the default command timeout of 4 seconds, you will need to increase the config since cy.intercept does not take a timeout option.

Cypress.config('defaultCommandTimeout', 6000)  // timeout in 6 seconds

cy.intercept('POST', '/route', (req) => {
  return new Promise(resolve => {
    setTimeout(() => resolve(req.continue()), 5000) // delay by 5 seconds
  })
}).as('delayedRequest')

// trigger POST

cy.wait('@delayedRequest')  

Cypress.config('defaultCommandTimeout', 4000)  // revert to normal timeout 

Middleware

If you already have a complex intercept, you can set the delay in a middleware intercept.

Middleware is always executed first, but if does not handle the request the call is passed to the next intercept.

// request is delayed here
cy.intercept('POST', '/route', {
    middleware: true                       // middleware always fires first
  }, 
  (req) => new Promise(resolve => 
    setTimeout(() => resolve(), 5000)      // no handler, just delay
  )
)

// then passed to here
cy.intercept('POST', '/route', 
  (req) => {
    req.continue()                            // handler for request
  }
).as('delayedRequest')

// trigger POST

cy.wait('@delayedRequest')  
Fody
  • 23,754
  • 3
  • 20
  • 37
  • For me this fails with `CypressError req.continue() was called after the request handler finished executing, but req.continue() can not be called after the request has already completed` - req.continue seems to be automatically called – Rory Mar 06 '22 at 11:12
  • Looks like it needs a Promise wrapper. – Fody Mar 06 '22 at 21:35
  • Thanks for looking at this! This does indeed delay the request, but unfortunately for my case this still sends it instantly after the delay rather than the throttling I need. – Rory Mar 07 '22 at 17:03
  • 1
    A delay in sending the request should allow Cypress to see the progress bar. – TesterDick Mar 07 '22 at 21:50
  • 1
    True, but I think @Rory wants to check intermediate progress steps. It depends on how what causes the progress bar to change. – Fody Mar 07 '22 at 22:13
  • 1
    In my case, I needed a way to delay an upload file request in order to test that I can cancel the upload using an abort controller mechanism. This worked for me. thank you. – forrestDinos Nov 15 '22 at 19:44
3

I think Cypress itself can only throttle or delay responses using cy.intercept() as mentioned in the question and described here: Throttle or delay response all incoming responses

However, you can probably use the Chrome Debugger Protocol from Chrome DevTools for that. Here you have the function Network.emulateNetworkConditions that allows you to throttle both download and upload throughput. This approach then assumes that your tests run in Chrome.

If you're unsure how to use this in your Cypress tests, here is a blog post about testing an application in offline network mode that uses a similar approach with Network.enable: Testing an Application in Offline Network Mode

Sebastiano Schwarz
  • 1,060
  • 2
  • 13
  • 32
  • This looks really promising, but when I use the example code from the recipe the second callback never resolves (`return Cypress.automation('remote:debugger:protocol' ......`) – Rory Mar 07 '22 at 17:30