1

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 father.

  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 fathers with certain criteria
    return cy.request('GET', fathersUrl).its('body').then(fatherObjects => {
        return fatherObjects.filter(father => father.childs.length && father.childs.find(sumCriteria))
    }).then(filteredFathers => {
        filteredFathers.forEach(father => {
            // For each father I retrieve a single child
            const targetChildId = father.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!

Gonzalo Diaz Ailan
  • 583
  • 1
  • 5
  • 23

1 Answers1

1

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
Fody
  • 23,754
  • 3
  • 20
  • 37