13

I have a single endpoint in the application. We hit the same api for each request with different action in the params.

URL:

/application/api

Sample Request Payload 1:

{
  "action": "CARD_TRANSACTION_HISTORY",
  "data": {
    "date_from": "2018-12-01",
    "date_to": "2018-12-31",
    "total": 5
  },
  "meta": {}
}

Sample Request Payload 2:

{
  "action": "CARD_BALANCE",
  "data": {
    "date_from": "2018-12-01",
    "date_to": "2018-12-31",
    "total": 5
  },
  "meta": {}
}

Sample Request Payload 3:

{
  "action": "CURRENCY_RATES",
  "data": {
    "date_from": "2018-12-01",
    "date_to": "2018-12-31",
    "total": 5
  },
  "meta": {}
}

the action in above request changes for different requests.

When the dashboard page is loaded, we trigger 3 concurrent AJAX POST requests with different actions.

Problem with cypress is you can only specify one response for a route, and other way to handle this is make sequential requests (which we can't do)

Even if we write response as a function it gets called only once.

Any ideas on how we can mock data on the basis of payload?

Digvijay Upadhyay
  • 709
  • 1
  • 11
  • 25
  • sorry, not understanding does each request have a query string or something that make them unique.. Is the action going in the the body? – Maccurt Dec 06 '18 at 15:48
  • Just edited the question with more sample payloads. Yes the action in payload is different and unique for each request – Digvijay Upadhyay Dec 06 '18 at 15:51
  • I was going to suggest writing the response as a function that returns one of the 3 payloads in order, until I read your last line. I think you might be SOL. – Brendan Dec 06 '18 at 17:37
  • Try this one : https://softans.com/question/cypress-stub-response-for-same-route-with-three-different-responses/ – GHULAM NABI Feb 08 '23 at 12:56

4 Answers4

6

I had the exact same problem and found @Richard Matsen's answer very useful, however when using the whitelist option it isn't possible to access proxy.request, which returns undefined. But if you use onRequest instead of whitelist, you can access the request and thus implement any action depending on that request's body.

So this should work:

cy.server({
  onRequest: (xhr) => {
    xhr.url = xhr.url + 
      xhr.request.body.action  == 'CARD_TRANSACTION_HISTORY' ? '?transactionHistory'
      : xhr.request.body.action  == 'CARD_BALANCE' ? '?balance'
      : xhr.request.body.action  == 'CURRENCY_RATES' ? '?currencyRates'
      : ''
  }
})
nicojonck
  • 291
  • 4
  • 9
  • You must be doing something wrong when accessing proxy in the whitelist callback, as it works in my test. – Richard Matsen Dec 17 '18 at 19:01
  • 1
    This is better than using whitelist, which was intended for filtering and feels a bit hacky. I tried altering response in `onRequest` and `onResponse` (to no avail) but didn't think to apply the URL parameter there. – Richard Matsen Dec 17 '18 at 19:02
  • 1
    So after setting this param in the request url, then what? I'm still not able to use `cy.route({ url: '...?balance' }).as('balance')` because Cypress will not find a match? – Aico Klein Ovink Jul 08 '19 at 09:20
  • @AicoKleinOvink were you able to get this working. I have all the steps in place but cypress can't match since the request don't have the query string attached! – atsituab Jul 23 '19 at 23:00
3

I did one dirty work around that worked, I didn't like it but I am out of options.

I simply combined all the responses in the same response.

My Mock Response

{
  balance: {..},
  transactionHistory: {..},
  currencyRates: {..}
}

The each response handler simply processes part it is interested in, if one of the response is array, we'll need to change it to an object.

I'll be on a lookout for a better work around.

Digvijay Upadhyay
  • 709
  • 1
  • 11
  • 25
3

Here is another hack. It relies on your api ignoring url parameters and that the cy.server whitelist function is called before the request is made.

cy.server({
  whitelist: (proxy) => {
    proxy.url = proxy.url + 
      proxy.request.body.action  == 'CARD_TRANSACTION_HISTORY' ? '?transactionHistory'
      : proxy.request.body.action  == 'CARD_BALANCE' ? '?balance'
      : proxy.request.body.action  == 'CURRENCY_RATES' ? '?currencyRates'
      : ''
  }
})

const apiMocks = {
  balance: {..},
  transactionHistory: {..},
  currencyRates: {..}
}
cy.route('/application/api?balance', apiMocks.balance).as('balance')
cy.route('/application/api?transactionHistory', apiMocks.transactionHistory)
  .as('transactionHistory')
cy.route('/application/api?currencyRates', apiMocks.currencyRates).as('currencyRates')

cy.visit(...)

cy.wait('@balance').then(xhr => //should see correct mock here )
Richard Matsen
  • 20,671
  • 3
  • 43
  • 77
1

if anyone comes to this question I probably found another hack, that worked for me (I'm using unfetch polyfill to work around fetch requests and cypress) - here is the code for one route.

cy.route({
  url: '/api/ngfw/devices/search',
  method: 'POST',

  // Default response, when parametrs doesn't match
  response: [],

  onResponse: (xhr) => {
    xhr.response = { body: [] };
    const { body } = xhr.request;

    if (body.groupBy) {
      const [groupBy] = body.groupBy;
      if (groupBy === 'overallColor') {
        Object.defineProperty(xhr.xhr, 'responseText', {

          // Putting response inside
          value: JSON.stringify(mockData.totalsResult),

          enumerable: true
        });
      }
    }
  },
});
SergeS
  • 11,533
  • 3
  • 29
  • 35