Gonzalo Diaz Ailan
Gonzalo Diaz Ailan

Reputation: 722

Handling multiple API calls of a single API call

With my team we are trying to implement a command for a really common operation for the business logic but I'm having issues handling its implementation. Basically:

  1. We have to retrieve an array of objects (GET).

  2. For each of that objects we have to retrieve (GET) another object inside its parent.

  3. For each of that sub-objects (childs) we have to check a condition and if it is the wanted condition we retrieve the child, otherwise we pass null.

Q: How do I handle multiple API calls that depends from a single API call without getting outside the CY chain?

This is my current implementation (doesn't works but kinda explains the wanted logic)

Cypress.Commands.add('myCommand', (sumCriteria: Function, anotherCriteria: Function) => {
    // I only retrieve parents with certain criteria
    return cy.request('GET', parentsUrl).its('body').then(parentObjects => {
        return parentObjects.filter(parent => parent.childs.length && parent.childs.find(sumCriteria))
    }).then(filteredParents => {
        filteredParents.forEach(parent => {
            // For each parent I retrieve a single child
            const targetChildId = parent.childs.find(sumCriteria).id;
            // For each single child I retrieve its data and evaluate if it has the needed criteria
            cy.request('GET', `${childsUrl}/${targetChildId}`)
                .its('body')
                .then(property => anotherCriteria(property))
        })
    });
})

Thanks in advance!

Upvotes: 2

Views: 2707

Answers (1)

Fody
Fody

Reputation: 31944

You almost have the correct pattern, but instead of returning results, put them on the queue.

Cypress does two things to make this work

  • in a custom command, it waits for any asynchronous commands to resolve
  • it returns whatever is on the queue at the last evaluation
Cypress.Commands.add('myCommand', (sumCriteria, anotherCriteria) => {

  cy.request('GET', fathersUrl)
    .its('body')
    .then(fatherObjects => {

      const filteredFathers  = fatherObjects.filter(father => {
        return father.childs.find(sumCriteria)
      });

      const results = []
      filteredFathers.forEach(father => { 
        cy.request('GET', father)              // waits for all these to resove
          .its('body')
          .then(property => anotherCriteria(property))   
      })

      cy.then(() => results)                   // returns this last queued command
  })
})

A reproducible example:

Cypress.Commands.add('myCommand', (sumCriteria, anotherCriteria) => {

  const fathersUrl = 'https://jsonplaceholder.typicode.com/todos/1'

  cy.request('GET', fathersUrl)
    .then(() => {

      // simulated url extraction
      const filteredFathers = [
        'https://jsonplaceholder.typicode.com/todos/2', 
        'https://jsonplaceholder.typicode.com/todos/3'
      ]

      const results = []
      filteredFathers.forEach(father => { 
        cy.request('GET', father)
          .then(res => {
            results.push(res.body.id)
          })
      });

      cy.then(() => results)
  });
})

cy.myCommand()
  .should('deep.eq', [2,3])             // ✅ passes

Upvotes: 3

Related Questions