Reputation: 1488
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 comes 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.com/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 compare 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.com/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.
Upvotes: 3
Views: 25841
Reputation: 1
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(() => {
// ...
});
});
});
});
});
Upvotes: -2
Reputation: 1488
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.com' })
try {
const { data } = await http.get('/myfile.xml')
let jdata = convert.xml2js(data, { compact: 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.
Upvotes: 4