最新消息:雨落星辰是一个专注网站SEO优化、网站SEO诊断、搜索引擎研究、网络营销推广、网站策划运营及站长类的自媒体原创博客

javascript - Cypress: create an it test foreach element of an array - Stack Overflow

programmeradmin1浏览0评论

I have an array of strings, which are links to different pages of the same website; for each of these webpages, i perform several tests upon it, and for each page I iterate on different HTML blocks of the page (all the pages have the same block ids).

For example, I have 10 pages that describes 10 different cities; all the pages have 9 identical html blocks with same id (they all es from the same template). I check if there are several words inside each of those ids.

I actually did it; what I need to do is to create an it test for each element. I'll try to explain better.

There are a series of arrays in the main object, so I have an urls object like:

urls: {
    cities: [...],
    secondArr: [...],
    thirdArr: [...]
}

I'm interested in the urls.cities array.

This is an object of the array of cities:

urls.cities: [{
        city: "Miami"
        discipline: "no-discipline"
        type: "main"
        url: "/"
    }, 
    ...]

I did something like that: main.test.js

it('should retrieve generated pages', () => {

    [...]

    // executing tests for cities
    urls.cities.forEach(elem => {
      cy.visit(elem.url)

      let ids = idsInAllPages   // there's a const idsInAllPages = ['id1', 'id2', ...] on top of the code
      ids.forEach(id => {
        // i get the webelement matching the id, with cypress
        cy.get(id).then(webElem => {
          checkAllWebElementsOfCityUrl(webElem, elem, id)
        })
      })
    })
})

In the checkAllWebElementsOfCityUrl() function I do several stuff, included a forEach loop to test each html block code (if needed I can add the code, it's a bit long to explain anyway because i take words to check from a JSON file and pare them with what's inside the html block).

Everything works fine, but my problem is that it's a single it('...') block: so if an error is found, all subsequent tests are not done.

So, I need to create an it test forEach element of the array.

Something like

it('should retrieve generated pages', () => {

    [...]

    // executing tests for cities
    urls.cities.forEach(elem => {
      it('should test a city', () => {
        cy.visit(elem.url)

        let ids = idsInAllPages
        ids.forEach(id => {
          cy.get(id).then(webElem => {
            webpageshelp.checkAllWebElementsOfCityUrl(webElem, elem, id)
          })
        })
      })
    })
})

But this code does not work, it skips entirely the it test inside loop. Is there any way to do it?

UPDATE:

i did not mention it before, but I discovered that the above code works. My issue was that, since I had to retrieve the urls list from an online XML file and convert it to an object (object urls described above), I wrapped all the code inside a request, followed by a .then(), like

cy.request('.xml').then(
urls => {
    urls.cities.forEach(elem => {
          it('should test a city', () => {
            cy.visit(elem.url)
          })
    })
})

Changing cy.request with whatever you want to retrieve the file, you always end up in a .then() block, even using cy.task.

NOR using the before() block can help, because there is NOT ANY WAY to bring data outside of the before() and inside the describe() block (not an it() block).

I finally found a solution, I will post down this page.

I have an array of strings, which are links to different pages of the same website; for each of these webpages, i perform several tests upon it, and for each page I iterate on different HTML blocks of the page (all the pages have the same block ids).

For example, I have 10 pages that describes 10 different cities; all the pages have 9 identical html blocks with same id (they all es from the same template). I check if there are several words inside each of those ids.

I actually did it; what I need to do is to create an it test for each element. I'll try to explain better.

There are a series of arrays in the main object, so I have an urls object like:

urls: {
    cities: [...],
    secondArr: [...],
    thirdArr: [...]
}

I'm interested in the urls.cities array.

This is an object of the array of cities:

urls.cities: [{
        city: "Miami"
        discipline: "no-discipline"
        type: "main"
        url: "https://myWebsite./miami/"
    }, 
    ...]

I did something like that: main.test.js

it('should retrieve generated pages', () => {

    [...]

    // executing tests for cities
    urls.cities.forEach(elem => {
      cy.visit(elem.url)

      let ids = idsInAllPages   // there's a const idsInAllPages = ['id1', 'id2', ...] on top of the code
      ids.forEach(id => {
        // i get the webelement matching the id, with cypress
        cy.get(id).then(webElem => {
          checkAllWebElementsOfCityUrl(webElem, elem, id)
        })
      })
    })
})

In the checkAllWebElementsOfCityUrl() function I do several stuff, included a forEach loop to test each html block code (if needed I can add the code, it's a bit long to explain anyway because i take words to check from a JSON file and pare them with what's inside the html block).

Everything works fine, but my problem is that it's a single it('...') block: so if an error is found, all subsequent tests are not done.

So, I need to create an it test forEach element of the array.

Something like

it('should retrieve generated pages', () => {

    [...]

    // executing tests for cities
    urls.cities.forEach(elem => {
      it('should test a city', () => {
        cy.visit(elem.url)

        let ids = idsInAllPages
        ids.forEach(id => {
          cy.get(id).then(webElem => {
            webpageshelp.checkAllWebElementsOfCityUrl(webElem, elem, id)
          })
        })
      })
    })
})

But this code does not work, it skips entirely the it test inside loop. Is there any way to do it?

UPDATE:

i did not mention it before, but I discovered that the above code works. My issue was that, since I had to retrieve the urls list from an online XML file and convert it to an object (object urls described above), I wrapped all the code inside a request, followed by a .then(), like

cy.request('https://myWebsite./urlslist.xml').then(
urls => {
    urls.cities.forEach(elem => {
          it('should test a city', () => {
            cy.visit(elem.url)
          })
    })
})

Changing cy.request with whatever you want to retrieve the file, you always end up in a .then() block, even using cy.task.

NOR using the before() block can help, because there is NOT ANY WAY to bring data outside of the before() and inside the describe() block (not an it() block).

I finally found a solution, I will post down this page.

Share Improve this question edited Apr 21, 2021 at 12:28 Don Diego asked Apr 20, 2021 at 14:36 Don DiegoDon Diego 1,5083 gold badges26 silver badges55 bronze badges 5
  • 1 Why not move the loop outside the it, urls.cities.forEach((elem) => it("...", () => ...))? – jonrsharpe Commented Apr 20, 2021 at 14:38
  • well, I can try, but would mean to execute code in the describe block (p.s.: the last block of code I wrote, the one with the it inside the loop, does not work) – Don Diego Commented Apr 20, 2021 at 14:42
  • @jonrsharpe I tried now but does not work. the blocks like urls.cities.forEach(elem => { it('TEST', () => { console.log('yayy') }) }) are never executed, sadly! – Don Diego Commented Apr 20, 2021 at 19:39
  • That should work, except change the console.log to expect(true).to.eq(true) - possible the tests are being ignored because they don't contain any assertions. – Richard Matsen Commented Apr 20, 2021 at 21:10
  • You are right, however I discovered that it's not that easy. For the sake of simplification, I did not mention that the urls list is taken by an xml file online and converted in an object. I think I will update the question, anyway the problem is that i cannot retrieve that file without using a .then() function, and put all the code inside of that .then() - if i put a var outside and I save the result in this var inside the .then(),data is not saved - . And I cannot do this, because I need to do multiple it tests. – Don Diego Commented Apr 20, 2021 at 22:21
Add a ment  | 

2 Answers 2

Reset to default 4

I finally found a solution:

since April,12, 2021 cypress added an experimental support to execute code before your spec file ( *.spec.js, *.test.js or whatever is your test file) starts.

More info here.

What is interesting is this option: experimentalInteractiveRunEvents, that enables the before:spec execution of code.

You need to add in your cypress.js file the option

{
  "...": "...",
  ...,

  "experimentalInteractiveRunEvents": true
}

then, in /plugins/index.js file, I added

module.exports = (on, config) => {
      on('...', ...),

    // executes function before running the test file - EXPERIMENTAL
      on('before:spec', async () => {
        let data = await webpageshelp.retrieveAndConvertXMLToJSON()
        return fs.writeFileSync('data.json', data)
      })
}

the async/await function is a function defined in another js file, that downloads and converts the XML file into a JSON:

async function retrieveAndConvertXMLToJSON() {
  const http = axios.create({ baseURL: 'http://myWebsite.' })

  try {
    const { data } = await http.get('/myfile.xml')
    let jdata = convert.xml2js(data, { pact: true, spaces: 4 })
    let rawUrls = []
    jdata.urlset.url.forEach(url => {
      rawUrls.push(url.loc._text)
    })
    return JSON.stringify(rawUrls)
  } catch (error) {
    throw err
  }
}

The logic about url.loc._text is due to the xml configuration; xml2js is a plugin to convert, well, xmls into a json.

at this point, is used in the main mytest.test.js file

const rawUrls = require('../../../data.json')

at the top of the file, then

describe('my tests', () => {
    // some more work with my urls
    let urls = filterUrls(rawUrls)    // a function that removes unuseful urls

    urls.cities.forEach(elem => {
      it(`TEST for city ${elem.city} and discipline ${elem.discipline}`, () => {
        console.log('yayyy', elem)
      })
    })
})

and VOILA, it runs!

the only drawback is that the file download happens only ONCE, when you click on your test file after running cypress open; so if you just change code or restart the test with the refresh button it won't download again the file. You have to close the browser and click again on your test, but this is acceptable for me, because the online file won't change so frequently.

Maybe it is a bit late but better late than never. We found out that you can use a forEach outside of an "it" so you can do a test multiple times with a different parameters.

import { array } from "../fixtures/fixtureFile";
describe("Questions field", () => {
    beforeEach(() => {
        cy.visit("/");
    });
    context("Structure", () => {
        array.forEach((el, index) => {
            it(` ${array[index]} 1st test `, () => {
                cy.get(array[index]).within(() => {
                    // ...
                });
            });
            it(` ${array[index]} 1st test `, () => {
                cy.get(array[index]).within(() => {
                    // ...
                });
            });
        });
    });
});
发布评论

评论列表(0)

  1. 暂无评论