1
votes

I have an API (let's call it getToken) to generate a token in its response body. I then call that token and store in the header of another API (let's call it returnBody). It makes sense to use localStorage for the getToken API as this token is re-usable for multiple API's. However, I am having doubts using localStorage if I need to return / display the response body of the succeeding API's such as returnBody. Inside the API's function/command, it logs the response body. However, when I call it via the test file, it generates null. Sample code below:

commands.js:

Cypress.Commands.add('getToken', () => { //API to generate token
    cy.request({
        method: 'POST',
        url: 'https://someAPItoGenerateToken',
        form: true, //sets to application/x-www-form-urlencoded
        body: {
            grant_type: 'credentials',
            scope: 'all-apis'
        },
        auth: {
            username: Cypress.env('api_username'),
            password: Cypress.env('api_password')
        }
    })
        .its('body')
        .then(bearerToken => {
            cy.setLocalStorage('bearerToken', JSON.stringify(bearerToken))
            cy.log('Token generated: ' + bearerToken.token)
            }
        )
})

Cypress.Commands.add('returnBody', (url, token) => { //API to return some values
    return cy.request({
        method: 'GET',
        url: url,
        auth: {
            bearer: token
        }
    })
        .then((response) => {
            // Stringify JSON the body.
            let body = JSON.stringify(response.body)
            cy.log(body)
        })
})

test file:

describe('Return value of 2nd API', ()=> {
    before(() => {
        cy.getToken() //Run this once to generate token for the entire test suite
        cy.saveLocalStorage()
    })

    beforeEach(() => {
        cy.restoreLocalStorage()
    })

    it('Return value of 2nd API', () => {
        cy.getLocalStorage('bearerToken').should('exist')
        cy.getLocalStorage('bearerToken').then(bearerToken => {
            const tokenJSON = JSON.parse(bearerToken)
            const url = 'https://someAPItoReturnJSONbody'
            cy.returnBody(url, tokenJSON.token).then((returned_value) => {
                cy.log(returned_value)
            })
        })

    })
})

body from the returnBody command returns the JSON response. However, returned_value from the test file displays null.

3

3 Answers

1
votes

As commented in this issue: "You cannot return a 3rd party promise from a custom command because this breaks the chaining between cypress commands. This is because the .then methods are not the same."

So, simply return the request body as:

Cypress.Commands.add('returnBody', (url, token) => {
  return cy.request({ /* options */ })
    .its("body");
});

Then, in your test you can do:

it("should return foo value", () => {
  cy.returnBody(url, token).then(returned_value => {
    cy.log(returned_value);
    expect(returned_value).to.deep.equal("foo-value");
  })
})
0
votes

You may need to return response body from your returnBody task:

Cypress.Commands.add('returnBody', (url, token) => {
  return cy.request({ /* options */ })
    .then(response => {
      let body = JSON.stringify(response.body);
      Cypress.log(body);
      return body; // Add this line
    });
});
0
votes

An alternative would be to store the token on cypress side using fixtures.

fixture.json:

{"bearerToken":""
.
.
.}

commands.js:

cy.fixture('testData.json').as('testData')
.
.
.then(bearerToken => {
            this.testData.bearerToken = JSON.stringify(bearerToken)
            cy.log('Token generated: ' + bearerToken.token)
            }
        )

test.js

describe('Return value of 2nd API', ()=> {
    before(() => {
        cy.getToken() //Run this once to generate token for the entire test suite
    })

    it('Return value of 2nd API', () => {
        cy.fixture('testData.json').as('testData')
        .then(testData => {
            const tokenJSON = JSON.parse(testData.bearerToken)
            const url = 'https://someAPItoReturnJSONbody'
            cy.returnBody(url, tokenJSON.token).then((returned_value) => {
                cy.log(returned_value)
            })
        })

    })
})