Dhamo
Dhamo

Reputation: 1251

how to store multiple elements' value under one root element in list of array with cypress

For the following result like structure

<div class="results">
  <div class="result">
    <div class="name">x</div>
    <div class="price">99</div>
  </div>
<div class="result">
    <div class="name">y</div>
    <div class="price">88</div>
  </div>
</div>

I want to store in an list of array like this

var resultList = [{"name":"x","amount":"99"}, {"name":"y","amount":"88"}]

And in cypress, I have the following code in cypress but it prints null and I am not sure how to resolve it?

let listOfResults = [];
let singleResult = {};
  cy.get('[class="results"]').each((resultItem) => {
    singleResult = {};

    //Retrive name
    cy.wrap(resultItem)
      .find('div[class$="name"]')
      .invoke("text")
      .then((val) => {
        cy.log("singleResult value for Title:" + val);
        singleResult["title"] = val;
        cy.log("singleResult::" + JSON.stringify(singleResult));//prints correctly
      });

   
    //Retrive price
    cy.wrap(resultItem)
      .find('div[class$="amount"]')
      .invoke("text")
      .then((val) => {
        cy.log("singleResult value for Title:" + val);
        singleResult["amount"] = val;
        cy.log("singleResult::" + JSON.stringify(singleResult));  //prints correctly          
      });
 });

 listOfResults.push(JSON.stringify(singleResult)); //singleResult became blank

 cy.log("list:" + JSON.stringify(listOfResults));

Upvotes: 1

Views: 1521

Answers (4)

Masoma Begum
Masoma Begum

Reputation: 1

Pull all data from dropdown list and then convert to array. Then click on desire label.

cy.get('ul[id="module-multiselect-options"] > li')
          .each(($ele) => {
            list.push($ele.text().trim())
          })
          .then(() => {
            cy.log(list[2])
            cy.get('ul[id="module-multiselect-options"] > li').contains(list[2]).click()
          })

Upvotes: 0

Seeker
Seeker

Reputation: 465

Just to clarify how to write JSON values to a file, as I couldn't convey properly in comments.

cy.writeFile('cypress/fixtures/example.json', `{ "name": "${val}" }`, {flag: 'a+'})
...
cy.writeFile('cypress/fixtures/example.json', `{ "amount": "${price}" }`, {flag: 'a+'})

Without the quotes, the json in the file is invalid and can't be read.

There is another problem, the final comma in the array causes a problem. Will post more if I can figure out how to avoid that issue.

Upvotes: 1

Alapan Das
Alapan Das

Reputation: 18618

You can also use a write file for this. Basically, you create an empty JSON under your fixtures file and then create the structure of the JSON you want. {flag: 'a+'} will append the data at the end of the file rather than replacing the contents.

cy.writeFile('cypress/fixtures/example.json', '') //Remove everything from the previous iteration
cy.writeFile('cypress/fixtures/example.json', '[') //Start Array
cy.get('[class="results"]').each((resultItem) => {
  //Retrive name
  cy.wrap(resultItem)
    .find('div[class$="name"]')
    .invoke('text')
    .then((val) => {
      cy.log('singleResult value for Title:' + val)
      cy.writeFile('cypress/fixtures/example.json', '{"name":', {flag: 'a+'})
      cy.writeFile('cypress/fixtures/example.json', `${val}`, {flag: 'a+'})
      cy.writeFile('cypress/fixtures/example.json', ',', {flag: 'a+'})
    })

  //Retrive price
  cy.wrap(resultItem)
    .find('div[class$="amount"]')
    .invoke('text')
    .then((price) => {
      cy.log('singleResult value for Title:' + price)
      cy.writeFile('cypress/fixtures/example.json', '"amount":', {flag: 'a+'})
      cy.writeFile('cypress/fixtures/example.json', `${price}`, {flag: 'a+'})
      cy.writeFile('cypress/fixtures/example.json', '},', {flag: 'a+'})
    })
})
cy.writeFile('cypress/fixtures/example.json', '\b', {flag: 'a+'}) //Remove last comma 
cy.writeFile('cypress/fixtures/example.json', ']', {flag: 'a+'}) //End Array

Upvotes: 0

Paolo
Paolo

Reputation: 5461

You must wrap code following your extractor in .then() because Cypress commands are asynchronous.

let listOfResults = [];
let singleResult = {};
cy.get('[class="results"]')
  .each((resultItem) => {
    ...
  })
  .then(() => {
    listOfResults.push(JSON.stringify(singleResult)); 
    cy.log("list:" + JSON.stringify(listOfResults));
  })

When you call cy.log() it always takes it's value before the test runs, unless it is inside a .then(). So cy.log("list:" + JSON.stringify(listOfResults)) sees the empty listOfResults.

Three more things:

Iterate class="result"

cy.get('[class="results"]') should be cy.get('[class="result"]') because class="results" is the overall div, but you want to iterate class="result" divs (assuming your cut-down HTML is accurate)

Push inside the .each()

listOfResults.push(JSON.stringify(singleResult)) should be inside the .each() because you are pushing the singleResult of each iteration. But you also need a .then() to do that, to ensure you push after singleResult is populated (same problem as above, async commands).

Don't stringify

You don't need to stringify for the logs, just add a comma. That way you keep the object structure for easier/better down-test usage.

const listOfResults = [];
// let singleResult = {};  // don't need this here

cy.get('[class="results"]').each((resultItem) => {

  const singleResult = {};   // declare it fresh each iteration

  //Retrive name
  cy.wrap(resultItem)
    .find('div[class$="name"]')   // also 'div.name'
    .invoke("text")
    .then((val) => {
      cy.log("singleResult value for Title:" + val);
      singleResult["title"] = val;
      cy.log("singleResult::", singleResult);
    });

  //Retrive price
  cy.wrap(resultItem)
    .find('div[class$="amount"]')  // also 'div.amount'
    .invoke("text")
    .then((val) => {
      cy.log("singleResult value for Title:" + val);
      singleResult["amount"] = val;         
      cy.log("singleResult::", singleResult);
    });

  // Now push to list
  cy.then(() => {
    listOfResults.push(singleResult)
  })
})
.then(() => {
  cy.log("list:", listOfResults);
})

Or use an alias

const listOfResults = [];
cy.get('[class="result"]').each((resultItem) => {
  ...
})
.then(() => {
  cy.wrap(listOfResults').as('listOfResults')  // save for later in the test
})

// later on  
cy.get('@listOfResults')
  .then(listOfResults => {
    cy.log("list:", listOfResults) // logs the value of parameter
                                   // not the outer listOfResults
  })

enter image description here

Upvotes: 2

Related Questions