cachingrediscypresse2e-testingvarnish

How to let Cypress-tests bypass all caches (Varnish, Redis, etc.)


On a site, I have a caching infrastructure like this:

After a deploy, I would like to receive a 'fresh' and uncached version of the site for my Cypress-tests. I don't want to clear the cache for all visitors. I just want my Cypress-tests to see an uncached version, after a deploy.

My idea was add a header to all my cy.visit and cy.request, and then setting up my Varnish-cache settings, to look for that. And handling it in Redis as well.

It appears to be quite cumbersome, but doable, to actually do this 'bypass-cache'-setup in Varnish and Redis. If anyone has a better/simpler way of doing this, I'm all ears.

What the core of this question is: 'How do I send a header with all my cy.visit and cy.request, without having to type it out like this, every time (see below)?

cy.request({
  method: 'GET',
  url: 'https://example.org',
  headers: {
    'x-my-custom-header': 'myValue'
  }
}).then((response) => {
  // ... 
});

cy.visit('/', {
  headers: {
    'x-my-custom-header': 'myValue'
  }});

My solution considerations

I considered building my own custom command on top of this. But it gets long-tailed and clumbsy quickly.

I also considered, if I could intercept all requests and add the header to it, in beforeEach in my e2e.js. I couldn't get this to work, though. This was my attempt:

beforeEach(() => {
  cy.intercept('*', (req) => {
    req.headers['x-foo'] = 'bar';
  });
});

Solution

  • Requests issued from the test (handled with intercept)

    You haven't mentioned the problem with the intercept, just that there is one. If there are other intercepts in the pipeline, the middleware option ensures the headers are added before those other intercepts are called.

    Passing a request to the next request handler

    // you could have a top-level middleware handler that
    // sets an auth token on all requests
    // but remember setting `middleware: true` will
    // cause this to always be called first
    cy.intercept('http://api.company.com/', 
      { middleware: true },              <--  don't block the intercepts in the tests
      (req) => {
        req.headers['authorization'] = `token ${token}`
      }
    )
    

    Example

    beforeEach(() => {
      cy.intercept('*', { middleware: true }, (req) => {
        req.headers['x-nocache'] = '123'
      }).as('header-added')
    })
    
    it('/todos/1 with additional header', () => {
    
      cy.intercept('https://jsonplaceholder.typicode.com/todos/1')
        .as('wait-for-response')
    
      cy.window().then(win => win.fetch('https://jsonplaceholder.typicode.com/todos/1'))
    
      cy.wait('@wait-for-response')
        .its('request')
        .its('headers')
        .its('x-nocache')
        .should('eq', '123')
    })
    
    it('/todos/2 with additional header', () => {
    
      cy.window().then(win => win.fetch('https://jsonplaceholder.typicode.com/todos/2'))
    
      cy.wait('@header-added')
        .its('request')
        .its('headers')
        .its('x-nocache')
        .should('eq', '123')
    })
    

    enter image description here


    Requests issued from the test (not handled by intercept)

    Using a custom command to add header to all cy.request()

    Cypress.Commands.overwrite("request", (originalFn, ...args) => {
      const {_} = Cypress
      const isMethod = (a) => ['GET','POST','PUT','PATCH','DELETE'].includes(a)
    
      let options = { headers: {} }
      if (_.isObject(args[0])) {
        _.extend(options, args[0])
      } else if (args.length === 1) {
        options.url = args[0]
      } else if (args.length === 2 && isMethod(args[0])) {
        options.method = args[0]
        options.url = args[1]
      } else if (args.length === 2 && _.isObject(args[1])) {  
        options.url = args[0]
        options.body = args[1]
      } else if (args.length === 3) {
        options.method = args[0]
        options.url = args[1]
        options.body = args[2]
      }
    
      options.headers['x-nocache'] = '123'
      return originalFn(options)
    })
    
    cy.request('https://jsonplaceholder.typicode.com/todos/3')
      .its('requestHeaders').should('have.property', 'x-nocache', '123')
    
    cy.request({url: 'https://jsonplaceholder.typicode.com/todos/3', headers:{'x-other': 'abc'}}) 
      .its('requestHeaders')
      .should(requestHeaders => {
        expect(requestHeaders['x-nocache']).to.eq('123')
        expect(requestHeaders['x-other']).to.eq('abc')
      })
    

    enter image description here