Cypress - how to send parameters to same function inside it's callback - javascript

I'm trying to implement fixtures in my cypress project to avoid repeatedly sending same requests.
Command "ReadFixture" returns data from fixture file:
Cypress.Commands.add("ReadFixture", (fixtureName, firstKey, secondKey = "") => {
let fixturePath = `cypress/fixtures/${fixtureName}.json`;
if (secondKey.length === 0) {
cy.readFile(fixturePath).then(fixture => {
let dataArray = [];
let fixtureKeys = Object.keys(fixture);
fixtureKeys.forEach(key => {
let data = fixture[key][firstKey];
dataArray.push(data);
});
return cy.wrap(dataArray);
});
}
else {
cy.readFile(fixturePath).then(fixture => {
let dataArray = fixture[secondKey][firstKey];
});
return cy.wrap(dataArray);
};
});
Data is in json structure:
{
"id_0": {
"id": "id_0",
"more_data": [
"string_0"
]
},
"id_1": {
"id": "id_1",
"more_data": [
"string_1",
"string_2"
]
}
}
For some tests, only "id" is required, such test example:
it("Level 1", () => {
cy.ReadFixture("fixture_name", "id").then(urlKeys => {
urlKeys.forEach(keyUrl => {
cy.request({
method: "GET",
url: `${reqUrl}/${keyUrl}`
}).then(response => {
expect(response.status).to.be.equal(200);
});
});
});
})
Everything works as expected, however, for other tests "more_data" of single "id" is required. My approach is to read fixture twice - first get array of "id", like in "Level 1" test, then get "more_data" for each "id" in array. Example:
it("Level 2", () => {
cy.ReadFixture("fixture_name", "id").then(urlKeys => {
urlKeys.forEach(keyUrl => {
cy.ReadFixture("fixture_name", "more_data", keyUrl).then(keyData => {
cy.request({
method: "GET",
url: `${reqUrl}/${keyUrl}/more_data`
}).then(response => {
expect(response.status).to.be.equal(200);
expect(response.body.more_data).to.be.eql(keyData);
});
});
});
});
});
Problem is, when
cy.ReadFixture("fixture_name", "more_data", keyUrl)
is called, keyUrl is not defined for it and command returns array of "more_data" from all "id" because of if statement. Also, keyUrl can't be passed to request. Is it possible to go around this issue or the method I'm using is completely wrong?

try changing your else block to wrap the values inside your then callback:
else {
cy.readFile(fixturePath).then(fixture => {
let dataArray = fixture[secondKey][firstKey];
return cy.wrap(dataArray);
});
};

Related

nodejs filtering an array of objects where the filtering is partially done in an async function

I've read many similar questions and have tried a bunch of code. Unfortunately, I'm not getting my code to run :-(
So, the situation is as follows: In a route of a node.js server, I have to respond with a filtered array of Objects. Unfortunately, whatever I do, I always get an empty array [] back. The filter is a bit tricky in my opinion, as it consists of a string comparison AND an async call to a library function. With the console output, I can clearly see that the correct element is found, but at the same time I see that I've already received the object...
Here is some code that exemplifies my challenge:
let testArray = [
{
id: 'stringId1',
data: {
someDoc: {
moreContent: 'Some random content',
type: 'noInterest'
}
}
},
{
id: 'stringId2',
data: {
someDoc: {
moreContent: 'Some random content',
type: 'ofInterest'
}
}
},
{
id: 'stringId3',
data: {
someDoc: {
moreContent: 'Some random content',
type: 'ofInterest'
}
}
}
]
// code from a library. Can't take an influence in it.
async function booleanWhenGood(id) {
if (id in some Object) {
return { myBoolean: true };
} else {
return { myBoolean: false };
}
}
// Should return only elements with type 'ofInterest' and that the function booleanWhenGood is true
router.get('/', function(res,req) {
tryOne(testArray).then(tryOneResult =>{
console.log('tryOneResult', tryOneResult);
});
tryTwo(testArray).then(tryTwoResult => {
console.log("tryTwoResult ", tryTwoResult);
});
result = [];
for (const [idx, item] of testArray.entries() ) {
console.log(idx);
if (item.data.someDoc.type === "ofInterest") {
smt.find(item.id).then(element => {
if(element.found) {
result.push(item.id);
console.log("ID is true: ", item.id);
}
});
}
if (idx === testArray.length-1) {
// Always returns []
console.log(result);
res.send(result);
}
}
})
// A helper function I wrote that I use in the things I've tried
async function myComputeBoolean(inputId, inputBoolean) {
let result = await booleanWhenGood(inputId)
if (result.myBoolean) {
console.log("ID is true: ", inputId);
}
return (result.myBoolean && inputBoolean);
}
// A few things I've tried so far:
async function tryOne(myArray) {
let myTmpArray = []
Promise.all(myArray.filter(item => {
console.log("item ", item.id);
myComputeBoolean(item.id, item.data.someDoc.type === "ofInterest")
.then(myBResult => {
console.log("boolean result", myBResult)
if (myBResult) {
tmpjsdlf.push(item.id);
return true;
}
})
})).then(returnOfPromise => {
// Always returns [];
console.log("returnOfPromise", myTmpArray);
});
// Always returns []
return(myTmpArray);
}
async function tryTwo(myArray) {
let myTmpArray = [];
myArray.forEach(item => {
console.log("item ", item.id);
myCompuBoolean(item.id, item.data.someDoc.type === "ofInterest")
.then(myBResult => {
console.log("boolean result", myBResult)
if (myBResult) {
myTmpArray.push(item.did);
}
})
});
Promise.all(myTmpArray).then(promiseResult => {
return myTmpArray;
});
}
Asynchronous programming is really tough for me in this situation... Can you help me get it running?
I didn't inspect your attempts that closely, but I believe you are experiencing some race conditions (you print return and print the array before the promises resolve).
However you can alwayd use a regular for loop to filter iterables. Like this:
let testArray = [
{
id: 'stringId1',
data: {
someDoc: {
moreContent: 'Some random content',
type: 'noInterest'
}
}
},
{
id: 'stringId2',
data: {
someDoc: {
moreContent: 'Some random content',
type: 'ofInterest'
}
}
},
{
id: 'stringId3',
data: {
someDoc: {
moreContent: 'Some random content',
type: 'ofInterest'
}
}
}
]
async function booleanWhenGood(id) {
if (id in { 'stringId1': 1, 'stringId2': 1 }) { // mock object
return { myBoolean: true };
} else {
return { myBoolean: false };
}
}
async function main() {
let filtered = []
for (item of testArray)
if ((await booleanWhenGood(item.id)).myBoolean && item.data.someDoc.type === 'ofInterest')
filtered.push(item)
console.log('filtered :>> ', filtered);
}
main()

how to print all students name which have percentage more than 70% in javascript?

I am using json-rule-engine .
https://www.npmjs.com/package/json-rules-engine
I am having a student list which have name and their percentage, Also I have business rule the percentage should be greater thank or equal to than 70 . so I want to print all students name those have percentage more than 70
here is my code
https://repl.it/repls/AlienatedLostEntropy#index.js
student list
const students = [
{
name:"naveen",
percentage:70
},
{
name:"rajat",
percentage:50
},
{
name:"ravi",
percentage:75
},
{
name:"kaushal",
percentage:64
},
{
name:"piush",
percentage:89
}
]
rule
engine.addRule({
conditions: {
all: [
{
fact: "percentage",
operator: "greaterThanInclusive",
value: 70
}
]
},
onSuccess(){
console.log('on success called')
},
onFailure(){
console.log('on failure called')
},
event: {
type: "message",
params: {
data: "hello-world!"
}
}
});
code
https://repl.it/repls/AlienatedLostEntropy#index.js
any update
The json-rules-engine module takes data in a different format. In your Repl.it you have not defined any facts.
Facts should be:
let facts = [
{
name:"naveen",
percentage:70
},
[...]
Also, the module itself doesn't seem to process an array of facts. You have to adapt it to achieve this. This can be done with:
facts.forEach((fact) => {
engine
.run(fact)
[...]
Finally, the student data is found inside the almanac. You can get these values with: results.almanac.factMap.get('[name|percentage|age|school|etc]').value
Here is the updated Repl.it: https://repl.it/#adelriosantiago/json-rules-example
I might have submitted a completely unrelated answer, but here goes. Since the students object is an array, you could just loop through it and then use an if else statement.
for (let i = 0; i < students.length; i++) {
if (students[i].percentage >= 70) {
console.log(students[i].name);
}
}
Sorry if this is incorrect!
Here is a working example.
Counting success and failed cases
const { Engine } = require("json-rules-engine");
let engine = new Engine();
const students = [
{
name:"naveen",
percentage:70
},
{
name:"rajat",
percentage:50
},
{
name:"ravi",
percentage:75
},
{
name:"kaushal",
percentage:64
},
{
name:"piush",
percentage:89
}
]
engine.addRule({
conditions: {
all: [{
fact: 'percentage',
operator: 'greaterThanInclusive',
value: 70
}]
},
event: { type: 'procedure_result'}
})
let result = {success_count : 0 , failed_count : 0}
engine.on('success', () => result.success_count++)
.on('failure', () => result.failed_count++)
const getResults = function(){
return new Promise((resolve, reject) => {
students.forEach(fact => {
return engine.run(fact)
.then(() => resolve())
})
})
}
getResults().then(() => console.log(result));

how to get data inside json object inside object?

i try to get some track_list data inside object JSON using Musixmatch API
here is my code
"body": {
"track_list": [
{
"track": {
"track_id": 194169151,
"track_name": "Blinding Lights",
"track_name_translation_list": [],
"track_rating": 100,
"commontrack_id": 104185748,
"instrumental": 0,
"explicit": 0,
"has_lyrics": 1,
"has_subtitles": 1,
"has_richsync": 1,
"num_favourite": 3237,
"album_id": 37216011,
"album_name": "After Hours",
"artist_id": 13937035,
"artist_name": "The Weeknd",
"track_share_url": "https://www.musixmatch.com/lyrics/The-Weeknd-3/Blinding-Lights?utm_source=application&utm_campaign=api&utm_medium=rickyreza%3A1409619798940",
"track_edit_url": "https://www.musixmatch.com/lyrics/The-Weeknd-3/Blinding-Lights/edit?utm_source=application&utm_campaign=api&utm_medium=rickyreza%3A1409619798940",
"restricted": 0,
"updated_time": "2020-04-10T08:31:57Z",
"primary_genres": {
"music_genre_list": [
{
"music_genre": {
"music_genre_id": 7,
"music_genre_parent_id": 34,
"music_genre_name": "Electronic",
"music_genre_name_extended": "Electronic",
"music_genre_vanity": "Electronic"
}
}
]
}
}
},
i just want to check if i can geat the data inside a track by doing lyric.album_name. and tried to get the album and i got this kind of things album_name as undefined. here is my main.js
main.js
function main() {
// initialize the data
const baseUrl = "https://api.musixmatch.com/ws/1.1";
const apiKey = "78fa4727ab9c4495d4fc07dae75f775b";
const chartTrack = "chart.tracks.get?chart_name=top&page=1&page_size=5&country=jp&f_has_lyrics=1"
const getLirik = () => {
fetch(`${baseUrl}/${chartTrack}&apikey=${apiKey}`)
.then(response => {
return response.json();
})
.then(responseJson => {
// console.log(responseJson);
// trackList.track_list = responseJson.message.body.track_list
console.log(responseJson.message.body.track_list.track);
// console.log(responseJson.message.body.track_list.track.album_name);
renderAllData(responseJson.message.body.track_list);
})
.catch(error => {
console.log(error);
})
}
/*
for making a new html DOM
*/
const renderAllData = (lyrics) => {
const lirikElement = document.querySelector("#popularLyrics");
lirikElement.innerHTML = "";
lyrics.forEach(lyric => {
lirikElement.innerHTML += `
<div>${lyric.album_name}</div>
`
})
}
getLirik();
}
export default main;
How do i can get all thos track_name and stuff inside track?
You forgot the property .track in your lyrics object. Try this
...
<div>${lyric.track.album_name}</div>
i checked the api call https://api.musixmatch.com/ws/1.1/chart.tracks.get?chart_name=top&page=1&page_size=5&country=jp&f_has_lyrics=1&apikey=78fa4727ab9c4495d4fc07dae75f775b the tracklist returns an Array of objects, where each object has only one key track
something like this track_list = [{track:{}},{track:{}}]
use ${lyric.track.album_name} it should work
you forgot one property, but you can do less nesting using destructuring in the function, this is a litle modification of your code:
const renderAllData = (trackList) => {
const lirikElement = document.querySelector("#popularLyrics");
lirikElement.innerHTML = "";
trackList.forEach(({ track }) => {
lirikElement.innerHTML += `
<div>${track.album_name}</div>
`;
});
};
renderAllData(data.body.track_list);

Edit query string parameter on retry

I've got the following code structure:
$.ajax({
type:"GET",
url:"http://example.com",
data:{
final:false
},
retry: {
attempt: 1,
limit: 3,
delay: 2000
},
success((data) => {
console.log("yay!");
}),
error((error) => {
if (this.retry.attempt++ <= this.retry.limit) {
var self = this;
if (self.retry.attempt > self.retry.limit) {
self.data.final = true;
}
setTimeout(() => {$.ajax(self)}, this.retry.delay);
}
})
});
The problem is that, when the request is called the first time, the data parameter gets erased, and its values appended to the url as a query string. So data no longer exists. The object that gets passed into the retry call is:
{
type:"GET",
url:"http://example.com?final=false",
retry: {
attempt: 2,
limit: 3,
delay: 2000
},
success((data) => {
//...
}),
error((error) => {
//...
}
}
How do I edit the final parameter of the request for the last retry?
You could try changing URL to "http://example.com?final=false" or "http://example.com?final="+false if false is a variable
try defining your request outside the ajax call:
const request = {
type:"GET",
url:"http://example.com",
data:{
final:false
}
};
$.ajax({...request, ...{
retry: {
attempt: 1,
limit: 3,
delay: 2000
},
success((data) => {
console.log("yay!");
}),
error((error) => {
if (this.retry.attempt++ <= this.retry.limit) {
const retryRequest = {...request, ...this};
if (this.retry.attempt > this.retry.limit) {
retryRequest.data.final = true;
}
setTimeout(() => {$.ajax(retryRequest)}, this.retry.delay);
}
})
});
alternatively, if you're only dealing with the one flag that only needs to be set on the last attempt:
$.ajax({
type:"GET",
url:"http://example.com",
retry: {
attempt: 1,
limit: 3,
delay: 2000
},
success((data) => {
console.log("yay!");
}),
error((error) => {
if (this.retry.attempt++ <= this.retry.limit) {
var self = this;
if (self.retry.attempt > self.retry.limit) {
self.data = {final: true};
}
setTimeout(() => {$.ajax(self)}, this.retry.delay);
}
})
});
also, fat arrow functions don't have local context, so your error function doesn't need to use self, i.e. this will work:
error((error) => {
if (this.retry.attempt++ <= this.retry.limit) {
if (this.retry.attempt > this.retry.limit) {
this.data = {final: true};
}
setTimeout(() => {$.ajax(this)}, this.retry.delay);
}
})

Filter by list of tests

What is the concise, functionally-oriented way of filtering a list of URLs where each item has to pass a list of tests? If the URL matches any of the tests, it should be filtered out.
I currently have:
var _ = require("underscore");
const anchors = [
{href:"https://example.org/contact"},
{href:"https://example.org/faq"},
{href:"https://example.org/contact"},
{href:"https://example.org/uploads/image-1024x1018.jpg"},
{href:"https://example.org/wp-json/oembed/1.0/embed?url=example"},
{href:"https://example.org/author/pm"},
{href:"https://example.org/wp/wp-login.php?action=lostpassword"},
{href:"https://example.org/wp/wp-login.php"},
{href:"https://example.org/feed"},
];
const tests = [
/\/wp\//,
/\/wp-json\//,
/\.jpg$/,
/\.png$/,
/\.gif$/,
]
function testAll(testString){
let pass = true;
_.each(tests, t => {
if(t.test(testString)) pass = false;
});
return pass;
}
console.log(anchors.map(anchor => {
return anchor.href;
}).filter(anchor => {
return testAll(anchor);
}));
But I suspect testAll can be done in a more concise way.
The solution I was looking for is actually some instead of every, because I actually need to reject the URL if it matches any of the tests:
console.log(anchors.map(anchor => {
return anchor.href;
}).filter(anchor => {
// return testAll(anchor);
return !_.some(tests, t => {
return t.test(anchor);
})
}));
You can use Array#every()
function testAll(testString){
return tests.every(reg => reg.test(testString));
}
You could work with Array#some and take the negated result of the checks for filtering.
var anchors = [{ href:"https://example.org/contact" }, { href:"https://example.org/faq" }, { href:"https://example.org/contact" }, { href:"https://example.org/uploads/image-1024x1018.jpg" }, { href:"https://example.org/wp-json/oembed/1.0/embed?url=example" }, { href:"https://example.org/author/pm" }, { href:"https://example.org/wp/wp-login.php?action=lostpassword" }, { href:"https://example.org/wp/wp-login.php" }, { href:"https://example.org/feed" }],
tests = [/\/wp\//, /\/wp-json\//, /\.jpg$/, /\.png$/, /\.gif$/],
result = anchors.filter(({ href }) => !tests.some(t => t.test(href)));
console.log(result);

Categories