Cypress - Create a new 'it' on each loop cycle - javascript

I'm automating the completion of a questionnaire via Cypress using fixtures. I need to create a new IT statement on each iteration of a loop running as currently if 1 set fails in the loop, then the rest of the dataset does not run.
Any help would be appreciated.
context('Questionnaire Completion', () => {
Cypress.Cookies.debug(true);
beforeEach(function() {
cy.fixture('data_set_4').then((testdata) => {
this.data = testdata
});
});
describe ('I will complete the form with each entry in the data set', function() {
it ('I will complete the form with each entry in the data set', function() {
for (let i = 0; i < this.data.testdata.length ; i++) {
participantcreation(this.data.testdata[i].DataEntry);
//I will enter a name and click Next
Name(this.data.testdata[i].DataEntry);
for (let j = 1; j < 31 ; j++) {
let answer = this.data.testdata[i]['Q' + j] - 1;
cy.get('#questionnaire_question_'+j+'_'+answer).click({ force: true });
}
});
});
});

Not entirely sure about what is being asked here but if you wish to create multiple 'its' depending on what you need you can just put it in a loop
describe('Tests', () => {
var i;
for (i = 0; i < 5; i++) {
it(`Test ${i}`, () => {
cy.wait(200)
});
}
});

Related

Matching strings don't trigger condition in Javascript

I am trying to do a very simple stock counting app, and display dynamically the stocks for each item. When I try to pair the button with its item, it doesn't work, even though the 2 strings are actually a match.
Here is the faulty function:
socket.on('Stock init', (data) => {
items = data.items;
displayStock(items);
});
function displayStock(items) {
fridgeItem.forEach((fridgeItem) => {
for (let i = 0; i < items.length; i++) {
var databaseProduct = items[i].name;
var databaseQuantity = items[i].chilled.toString();
console.log(fridgeItem.textContent, databaseProduct);
}
if (fridgeItem.textContent === databaseProduct) {
fridgeItem.textContent = databaseProduct + ' ' + databaseQuantity;
} else {
console.log('err');
}
});
}
Answer might be stupid but I can't see it at the moment. Thanks

Deferred objects with multiple getJSON calls that depend on each other

I am making 3 get JSON calls in myFunction() that gets called on a button click. 2 of these getJSONs depend on each other for their execution. It basically parses through 10 web pages and collects some data. With this data it will go to another page and collect some other data. I want to display "DONE" at the end of myFunction so the user knows that we have finally got all data and the search operation is complete. However these calls, I think, are asynchronous, so I use deferred objects. But even though I pass all calls to $.when.apply(call1,call2,call3), it displays the "DONE" before any data is printed on the console. Once it prints "DONE", then it starts to print the results. How can I modify my code so that I would be able to display "DONE" only when myFunction has ran completely for all 10 pages and has printed all data on the console.
var call1 = [];
var call2 = [];
var call3 = [];
function myFunction() {
data3 = [];
url = ''; // some URL here
call3.push($.getJSON(url, function(data4) {
data3 = data4;
}));
for (var page = 1; page < 10; page++) {
(function(page) {
url = 'http://example.com/' + page;
call1.push($.getJSON(url, function(data1) {
for (var num = 0; num < data1.standings.results.length; num++) {
(function(num) {
url = 'http://example.com/' + data1.entry[num];
call2.push($.getJSON(url, function(data2) {
for (var i = 0; i < 15; i++) {
(function(i) {
console.log(data3.ele[(data2.p[i].element) - 1].x);
return;
}
})(i);
}
})
);
})(num);
}
})
);
})(page);
};
$.when.apply(call1, call2, call3).then(function() {
console.log("DONE");
});
}
I was finally able to solve this problem. As mentioned in the comments, we need to chain various promises and the function structure should match the structure of the when command. So, in the function as call1 was pushed first, I need to call the when command for call1 and then nest the subsequent commands and so on.
var call1 = [];
var call2 = [];
var call3 = [];
function myFunction() {
data3 = [];
url = ''; // some URL here
call3.push($.getJSON(url, function(data4) {
data3 = data4;
}));
for (var page = 1; page < 10; page++) {
(function(page) {
url = 'http://example.com/' + page;
call1.push($.getJSON(url, function(data1) {
for (var num = 0; num < data1.standings.results.length; num++) {
(function(num) {
url = 'http://example.com/' + data1.entry[num];
call2.push($.getJSON(url, function(data2) {
for (var i = 0; i < 15; i++) {
(function(i) {
console.log(data3.ele[(data2.p[i].element) - 1].x);
return;
}
})(i);
}
})
);
})(num);
}
})
);
})(page);
};
$.when.apply($, call1).then(function(){
$.when.apply($, call2).then(function(){
document.getElementsByName('output')[0].value+="Search Completed"+'\r\n';
});
});
}

How do I fill an array with multiple api calls?

I'm getting stuck trying to figure out how to fill this 'artists' array with consecutive calls to the spotify api. I'm trying to fill the 'artists' array with every artist in the user's saved library. The way the call works is, it will return a chunk of max 50 artists per call. However when I fill the array here and then try to pass a new i value to the getUserData function, I don't think the i value is updated and for some reason my 'artists' array is filling up to only 226 while the 'total' value is 276 for my test case. Can someone help me with this? I think it may be a scope issue but I can't figure out where. Thank you in advance!
var i = 0;
function getUserData(accessToken, i) {
return $.ajax({
url: 'https://api.spotify.com/v1/me/tracks?limit=50&offset=' + i,
headers: {
'Authorization': 'Bearer ' + accessToken
}
});
}
var artists = [];
loginButton.addEventListener('click', function() {
login(function(accessToken) {
getUserData(accessToken, i)
.then(function(response) {
var total = response.total;
while(i < total) {
i+=50;
getUserData(accessToken, i)
.then(function(response) {
total = response.total;
alert(total); //testing
loginButton.style.display = 'none';
for (var j = 0; j < 50 && artists.length < total; j++){
artists.push(response.items[j].track.album.artists[0].name);
}
alert(artists[7] + artists.length); //testing
alert(artists[artists.length - 1]); //testing
});
}
});
});
});
Thank you for your help!
your first getUserData in the while loop starts at i = 50 - so the first 50 wont be pushed into artists array
add
for (var j = 0; j < 50 && artists.length < total; j++) {
artists.push(response.items[j].track.album.artists[0].name);
}
before the while loop - like so
var artists = [];
loginButton.addEventListener('click', function() {
login(function(accessToken) {
var i = 0;
getUserData(accessToken, i)
.then(function(response) {
var total = response.total;
// added code to store the first 50 artists
for (var j = 0; j < 50 && artists.length < total; j++) {
artists.push(response.items[j].track.album.artists[0].name);
}
// end added code
while (i < total) {
i += 50;
getUserData(accessToken, i)
.then(function(response) {
total = response.total;
alert(total); //testing
loginButton.style.display = 'none';
for (var j = 0; j < 50 && artists.length < total; j++) {
artists.push(response.items[j].track.album.artists[0].name);
}
alert(artists[7] + artists.length); //testing
alert(artists[artists.length - 1]); //testing
});
}
});
});
});
The only issue with this is that the order of artists could conceivably be "wrong", in that one of the asynchronous getUserData could take longer than the next one (network issues are unpredictable)
You can rewrite your code to use Array#map, Array#concat and $.when - and preserve the order of data by doing so
I'm assuming response.total is the total number of tracks, regardless of the offset and limit as this is implied by your code
loginButton.addEventListener('click', function() {
login(function(accessToken) {
loginButton.style.display = 'none';
var arr = [getUserData(accessToken, i)];
arr[0]
.then(function(response) {
for (var i = 50; i < response.total; i += 50) {
arr.push(getUserData(accessToken, i));
}
$.when.apply($, arr)
.then(function() {
var artists = [].concat.apply([], [].map.call(arguments, function(response) {
return response.items.map(function(item) {
return item.track.album.artists[0].name;
});
}));
// artists array is filled, now do what you need
});
})
.catch(function(err) {
// handle errors here
});
});
});
As above but using native Promise.all
loginButton.addEventListener('click', function() {
login(function(accessToken) {
loginButton.style.display = 'none';
var arr = [getUserData(accessToken, i)];
arr[0]
.then(function(response) {
for (var i = 50; i < response.total; i += 50) {
arr.push(getUserData(accessToken, i));
}
Promise.all(arr).then(function(chunks) {
var artists = [].concat.apply([], chunks.map(function(response) {
return response.items.map(function(item) {
return item.track.album.artists[0].name;
});
}));
// artists array is filled, now do what you need
});
})
.catch(function(err) {
// handle errors here
});
});
});

How to find out when files traverse is over?

I want to create my own drag and drop component and i use this:
Does HTML5 allow drag-drop upload of folders or a folder tree?
everything works fine, but i want to find out when files traverse is over, because i want to create an array of string, which contains file names and then do sth.
traverseFileTree function contains callbacks so there are async tasks. I need to wait until callbacks are done and i don't know how to do it. I would use jQuery.Deffered but traverseFileTree is called from a loop and -in addition- this is a recursive function.
this.dndElement.addEventListener('drop', (ev: any) => {
var items = ev.dataTransfer.items;
for (var i = 0; i < items.length; i++) {
this.traverseFileTree(items[i].webkitGetAsEntry());
}
// i want to wait until callbacks in the traverseFileTree are done and do sth with fileNames array
});
public traverseFileTree(item, path?) {
path = path || "";
if (item.isFile) {
item.file((file) => {
this.fileNames.push(file.name);
});
} else if (item.isDirectory) {
var dirReader = item.createReader();
dirReader.readEntries((entries) => {
for (var j = 0; j < entries.length; ++j) {
this.traverseFileTree(entries[j], path + item.name + "/");
}
});
}
}
UPDATE:
I did sth like this:
this.dndElement.addEventListener('drop', (ev: any) => {
var items = ev.dataTransfer.items;
for (var i = 0; i < items.length; i++) {
var item = items[i].webkitGetAsEntry();
if (item) {
this.traverseFileTree(item);
}
}
Promise.all(this.promises).then(() => { console.log(this.fileNames); });
});
public traverseFileTree(item, path?) {
path = path || "";
console.log(item);
if (item.isFile) {
this.fileNames.push(item.fullPath);
} else if (item.isDirectory) {
this.promises.push(new Promise((resolve, rejected) => {
var dirReader = item.createReader();
dirReader.readEntries((entries) => {
for (var j = 0; j < entries.length; ++j) {
this.traverseFileTree(entries[j], path + item.name + "/");
}
resolve();
})
}));
}
}
and now i get list of files ... but only from "first level".
Adir
Bdir
1File
2File
Cdir
3File
4File
5File
6File
And i get 1File,2File,5File,6File but 3File,4File don't. Why?
You kick off here :
this.traverseFileTree(items[i].webkitGetAsEntry());
And then the function public traverseFileTree(item, path?) { takes over. The function is sync (even though its recursive). Once it returns you should already be in good shape 🌹
However please note that this is webkit only :https://developer.mozilla.org/en/docs/Web/API/DirectoryReader#readEntries and is likely to be removed at some point.

returning a value after for loops

So, I have been trying for the past few hours to get an result out of a function after performing some for loops :
Cluster.prototype.initiate_api_data_fetching = function(username) {
var self = this,
object = [];
return self.initiate_available_market_search(username, function(data_object){
var json_obj = JSON.parse(data_object);
for(var obj_key in json_obj) {
for (var i = json_obj[obj_key].length - 1; i >= 0; i--) {
self.initiate_market_items_data_fetching(username, json_obj[obj_key][i].site, function(data_obj){
var json_object = JSON.parse(data_obj);
for(var data_key in json_object) {
for (var j = json_object[data_key].length - 1; j >= 0; j--) {
object.push(json_object[data_key][j]);
/*log(object);*/
};
};
log(object);
});
};
};
});
};
Making abstraction of all the variables and other things that make no sense to you readers, I would just like to know how can I return the object array with the data that I\m pushing in it. Everything is fine if I\m logging where the /*log(object);*/ is, but if I want to see what the object contains at the end of the function, I get an empty array.
I suggest you add a callback to your main function and call it when done..
Cluster.prototype.initiate_api_data_fetching = function (username, callback) {
var self = this,
object = [];
return self.initiate_available_market_search(username, function (data_object) {
var json_obj = JSON.parse(data_object)
, counter = 0;
function done() {
counter -= 1;
if (counter === 0) {
callback(object);
}
}
for (var obj_key in json_obj) {
if (!json_obj.hasOwnProperty(obj_key)) { continue; }
for (var i = json_obj[obj_key].length - 1; i >= 0; i--) {
counter += 1;
self.initiate_market_items_data_fetching(username, json_obj[obj_key][i].site, function (data_obj) {
var json_object = JSON.parse(data_obj);
for (var data_key in json_object) {
if (!json_object.hasOwnProperty(data_key)) { continue; }
for (var j = json_object[data_key].length - 1; j >= 0; j--) {
object.push(json_object[data_key][j]);
/*log(object);*/
}
}
done();
});
}
}
});
};
PS. 1 assumption is that initiate_api_data_fetching is async.
PS. 2 Follow the advice from the commenters above to improve your code. I answered your immediate question by showing you how to synchronise async calls, but don't stop there.

Categories

Resources