How to Receive cypress interception fixture twice but in different format? - javascript

I'm a beginner with Cypress and I am stuggeling with the following:
I am calling the following from my test file:
cy.SetupClassificationsStubFixture(1);
This refers to the following command:
Cypress.Commands.add("SetupClassificationsStubFixture", (amount) => {
cy.fixture('ClassificationsStub.json').then(classificationsStub => {
const slicedClassifications = classificationsStub.slice(0, amount)
cy.intercept('GET', '**/api/classifications', slicedClassifications)
}).as('classifications')
cy.fixture('ClassificationsStub.json').then(classificationsStub => {
const slicedClassifications = classificationsStub.slice(0, amount)
cy.intercept('GET', '**/api/classifications/*', slicedClassifications)
}).as('classifications') });
As you can see this customcommand is intercepting 2 api request. Namely:
** /api/classifications
** /api/classifications/ *
The first interception is with []
The second intercecption needs exactly the same response buth without brackets []
But you already understand that both respons are now with brackets '[]'. I tried to make a second fixture file without [], but the the slice function is not working.
So I need:
The second intercept like:
{
"id": "9d4a9c14-ef37-4a64-a1eb-63ab45cdf530",
"name": "CypressTest",
"userRole": "Editor",
"childClassifications": []
}
But I get:
[{
"id": "9d4a9c14-ef37-4a64-a1eb-63ab45cdf530",
"name": "CypressTest",
"userRole": "Editor",
"childClassifications": []
}]
How could I get this array back without []? Thankyou indeed!
UPDATE:
I am trying now the following:
Cypress.Commands.add("SetupClassificationsStubFixture", (amount) => {
cy.fixture('ClassificationsStub.json').then(classificationsStub => {
const slicedClassifications = classificationsStub.slice(0, amount)
cy.intercept('GET', '**/api/classifications', slicedClassifications)
}).as('classifications')
cy.fixture('ClassificationsStub.json').then(classificationsStub => {
const slicedClassifications = classificationsStub.slice(0, amount)
const arr2 = slicedClassifications
const obj5 = Object.fromEntries(arr2)
cy.intercept('GET', '**/api/classifications/*', obj5)
}).as('classification')});
But this give me a emtpy .json file. It the respons is not just ' {} '

There looks to be some brackets out of place in the code.
I would also recommend using different alias names, otherwise the calls to cy.wait('#classifications') may not work as you expect.
Cypress.Commands.add("SetupClassificationsStubFixture", (amount) => {
cy.fixture('ClassificationsStub.json').then(classificationsStub => {
const slicedClassifications = classificationsStub.slice(0, amount)
cy.intercept('GET', '**/api/classifications', slicedClassifications)
.as('classifications')
const firstClassification = slicedClassifications[0] // just first item
cy.intercept('GET', '**/api/classifications/*', firstClassification)
.as('firstClassification')
})
});

So in case, your data looks like this:
var data = [{
"id": "9d4a9c14-ef37-4a64-a1eb-63ab45cdf530",
"name": "CypressTest",
"userRole": "Editor",
"childClassifications": []
}]
So to extract the data with just curly braces you can do data[0].
{
"id": "9d4a9c14-ef37-4a64-a1eb-63ab45cdf530",
"name": "CypressTest",
"userRole": "Editor",
"childClassifications": []
}
Working example console screenshot:

Finally solved with:
Cypress.Commands.add("SetupClassificationsStubFixture", (amount) => {
cy.fixture('ClassificationsStub.json').then(classificationsStub => {
const slicedClassifications = classificationsStub.slice(0, amount)
cy.intercept('GET', '**/api/classifications', slicedClassifications)
}).as('classifications')
cy.fixture('ClassificationsStub.json').then(classificationsStub => {
cy.intercept('GET', '**/api/classifications/*', (req) => {
const slicedClassifications = classificationsStub.slice(0, amount)
var selectedClassification = req.url.replace(new RegExp('.*api/classifications/'), '');
let foundClassification = FindChildClassification(selectedClassification, slicedClassifications);
//If not found return first always
req.reply({
statusCode: 200,
body: foundClassification ?? slicedClassifications[0]
})
})
}).as('classification')
And
export function FindChildClassification(id, classificationArray) {
for (let i = 0; classificationArray.length - 1 >= i; i++) {
if (classificationArray[i].id == id) {
return classificationArray[i];
} else {
if (classificationArray[i].childClassifications.length > 0) {
let childClassification = FindChildClassification(id, classificationArray[i].childClassifications);
if (childClassification != null) { return childClassification }
}
}
}
return null;
}

Related

Run async/await function inside a reduce Javascript [duplicate]

This question already has answers here:
JavaScript array .reduce with async/await
(11 answers)
Closed 6 months ago.
I need to fetch values from another API using the guid inside this particular array, then group them together (hence I used reduce Javascript in this case)
However, I could not get those values sumEstimatedHours and sumWorkedHours as expected. Can someone suggest a method please?
export const groupProjectsByPM = (listOfProjects) => {
const dir = "./json";
const estimatedHours = fs.existsSync(dir)
? JSON.parse(fs.readFileSync("./json/phases.json", "utf-8"))
: null;
let sumWorkedHours, sumEstimatedHours;
const groupedProjects = listOfProjects?.reduce(
(
group,
{
guid,
projectOwner: { name: POName },
name,
customer: { name: customerName },
deadline,
calculatedCompletionPercentage,
}
) => {
listOfProjects.map(async (element, index) => {
// const element = listOfProjects[index];
sumWorkedHours = await getWorkhoursByProject(element?.guid).then(
(res) => {
return res.reduce((acc, cur) => {
return acc + cur.quantity;
}, 0);
}
);
const filteredEstimatedHours = estimatedHours.filter(
(item) => item.project.guid === element.guid
);
sumEstimatedHours = filteredEstimatedHours.reduce((acc, cur) => {
return acc + cur.workHoursEstimate;
}, 0);
group[POName] = group[POName] || [];
group[POName].push({
guid,
name,
POName,
customerName,
deadline,
calculatedCompletionPercentage,
sumEstimatedHours,
sumWorkedHours,
});
return group;
});
return group;
},
[]
);
return groupedProjects;
};
here is an example of async/await inside reduce:
let's assume that we have an array of numbers
const arrayOfNumbers = [2,4,5,7,6,1];
We are going to sum them using reduce function:
const sumReducer = async () => {
const sum = await arrayOfNumbers.reduce(async (promisedSum, num) => {
const sumAcc = await promisedSum
// any promised function can be called here..
return sumAcc + num
}, 0)
console.log(sum)
}
So the trick is to remember to await the accumulator inside the reduce function
export const groupProjectsByPM = async (listOfProjects) => {
const dir = "./json";
const estimatedHours = fs.existsSync(dir)
? JSON.parse(fs.readFileSync("./json/phases.json", "utf-8"))
: null;
let sumWorkedHours, sumEstimatedHours;
const groupedProjects = await listOfProjects?.reduce(
async (
promisedGroup,
{
guid,
projectOwner: { name: POName },
name,
customer: { name: customerName },
deadline,
calculatedCompletionPercentage,
}
) => {
listOfProjects.map(async (element, index) => {
//accumulator in your case is group
const group = await promisedGroup;
// const element = listOfProjects[index];
sumWorkedHours = await getWorkhoursByProject(element?.guid).then(
(res) => {
return res.reduce((acc, cur) => {
return acc + cur.quantity;
}, 0);
}
);
const filteredEstimatedHours = estimatedHours.filter(
(item) => item.project.guid === element.guid
);
sumEstimatedHours = filteredEstimatedHours.reduce((acc, cur) => {
return acc + cur.workHoursEstimate;
}, 0);
group[POName] = group[POName] || [];
group[POName].push({
guid,
name,
POName,
customerName,
deadline,
calculatedCompletionPercentage,
sumEstimatedHours,
sumWorkedHours,
});
return group;
});
return group;
},
[]
);
return groupedProjects;
};
Best of luck ...

forEach objects value equals undefined

Data Received from firebase-realtime-database as following
{
"0": {
"id": 10250903,
...
},
"1": {
"id": 10810490,
...
},
...
}
Code to setProducts
const [products, setProducts] = useState();
const {isLoading, setIsLoading} = useData();
useEffect(async () => {
setIsLoading(true);
await firebase
.database()
.ref('events')
.once('value')
.then((events) => {
setProducts(events);
})
.finally(() => setIsLoading(false));
}, []);
I tried to iterate the object to get the values
products?.forEach((product) => {
console.log(product);
});
Result:
Object {
"id": 10250903,
...
}
Object {
"id": 10810490,
...
}
But when I try to access id values, console prints undefined
products?.forEach((product) => {
console.log(product.id); // undefined
});
undefined
undefined
I am stuck I tried everything.
Object.values(products) will not work since product will be undefined until data is received.
Creating a new Array and mapping into it also will not work.
You stated that:
Object.values(products) will not work since product will be undefined until data is received.
I think you are very close to the solution.
Using products || {} handles the case where products are undefined or null.
var products = {
"0": {
"id": 10250903,
},
"1": {
"id": 10810490,
},
}
Object.values(products || {}).forEach(p => console.log(p.id))
If you are transforming products into a new products collection, reduce may become useful:
Object.values(products || {}).reduce((acc, p) => {
acc[p.id] = p;
return acc;
}, {})
=>
{
"10250903": {
"id": 10250903,
...
},
"10810490": {
"id": 10810490,
...
}
}
Or:
Object.values(products || {}).reduce((acc, p) => {
acc.push(p.id)
return acc;
}, [])
=>
[10250903, 10810490]

undefined after setState() (use hook) in Reactjs

I learn react and js myself. please explain why this situation occurs. PS: excuse me for the large text, I tried to explain the problem as clearly as possible. thanks. Essence of the matter: set the initial state through the hook:
const [pokemon, setPokemon] = useState({
img: "",
name: "",
types: [],
abilities: [],
moveList: [],
weight: "",
height: "",
description: "",
genus: "",
chanceToCatch: "",
evolutionURL: ""
});
further I make api requests to get information from inside useEffect:
useEffect(() => {
const fetchData = async () => {
await Axios({
method: "GET",
url: urlPokemonAPI
})
.then(result => {
const pokemonResponse = result.data;
/* Pokemon Information */
const img = pokemonResponse.sprites.front_default;
const name = pokemonResponse.name;
const weight = Math.round(pokemonResponse.weight / 10);
const height = pokemonResponse.height / 10;
const types = pokemonResponse.types.map(type => type.type.name);
const abilities = pokemonResponse.abilities.map(
ability => ability.ability.name
);
const moveList = pokemonResponse.moves.map(move => move.move.name);
setPokemon(() => {
return {
img: img,
name: name,
weight: weight,
types: types,
abilities: abilities,
moveList: moveList,
height: height
};
});
})
await Axios({
method: "GET",
url: urlPokemonSpecies
}).then(result => {
let description = "";
result.data.flavor_text_entries.forEach(flavor => {
if (flavor.language.name === "en") {
description = flavor.flavor_text;
}
});
let genus = "";
result.data.genera.forEach(genera => {
if (genera.language.name === "en") {
genus = genera.genus;
}
});
const evolutionURL = result.data.evolution_chain.url;
const eggGroups = result.data.egg_groups.map(
egg_group => egg_group.name
);
const chanceToCatch = Math.round(
(result.data.capture_rate * 100) / 255
);
setPokemon(pokemon => {
return {
...pokemon,
description: description,
genus: genus,
chanceToCatch: chanceToCatch,
evolutionURL: evolutionURL,
eggGroups: eggGroups
};
});
});
};
fetchData();
}, [urlPokemonAPI, urlPokemonSpecies]);
The problem arises specifically with eggGroups (with identical handling of abilities and types there is no such problem). And this is what happens when I want to output data to a page as <div> Egg Group: {pokemon.eggGroups} </div> the data is displayed normally, but as soon as I want to output eggGroups as well as abilities and types separated by commas (join ( ',')) - error: TypeError: pokemon.eggGroups is undefined. I decided to check this matter through the console and stuffed this eggGroups key into the timeout:
At some point, eggGroups becomes undefined ... why, I can’t understand. But if I set the state separately, like const [egg, setEgg] = useState ([]); setEgg (eggGroups); such a problem is not observed. why is this happening? everything was fine with types and abilities. Thank you in advance.
state updater from hooks doesn't merge the state values when updating state, instead it just replaces the old value with new one
Since you use state updater like
setPokemon(() => {
return {
img: img,
name: name,
weight: weight,
types: types,
abilities: abilities,
moveList: moveList,
height: height
};
});
eggGroups property is lost and hence it becomes undefined. You need to update it by spreading the previous state values obtained from callback
setPokemon((prev) => {
return {
...prev
img: img,
name: name,
weight: weight,
types: types,
abilities: abilities,
moveList: moveList,
height: height
};
});
Your code have a problem, this is the proper way to do await with axios,
you need to import axios like this
import axios from 'axios';
the await should be call with a promise, then it return the data from api like this:
const result = await axios.get(urlPokemonAPI);
This is the code snippet with the same logic to your code
useEffect(() => {
const fetchData = async () => {
// import axios from 'axios';
try {
const result = await axios.get(urlPokemonAPI);
const pokemon = result.data;
setPokemon({
img: pokemon.sprites.front_default,
name: pokemon.name,
weight: Math.round(pokemon.weight / 10),
types: pokemon.types.map(i => i.type.name),
abilities: pokemon.abilities.map(i => i.ability.name),
moveList: pokemon.moves.map(i => i.move.name),
height: pokemon.height / 10
});
const result2 = await axios.get(urlPokemonSpecies);
const data = result2.data;
let description = "";
data.flavor_text_entries.forEach(i => {
const lang = i.language.name
if (lang === "en") {
description = i.flavor_text;
}
});
let genus = "";
data.genera.forEach(i => {
const lang = i.language.name;
if (lang === "en") {
genus = i.genus;
}
});
setPokemon(pokemon => {
return {
...pokemon,
description,
genus,
chanceToCatch: Math.round((data.capture_rate * 100) / 255),
evolutionURL,
eggGroups: data.egg_groups.map(g => g.name)
};
});
} catch (e) {
console.log(e);
}
};
fetchData();
}, [urlPokemonAPI, urlPokemonSpecies]);
do you see another problem: you call setPokemon two times, let's rewrite it again:
useEffect(() => {
const fetchData = async () => {
// import axios from 'axios';
try {
const result = await axios.get(urlPokemonAPI);
const data1 = result.data;
const result2 = await axios.get(urlPokemonSpecies);
const data2 = result2.data;
function resolveDescription(data) {
let description = "";
data.flavor_text_entries.forEach(i => {
const lang = i.language.name
if (lang === "en") {
description = i.flavor_text;
}
});
return description;
}
function resolveGenus(data) {
let genus = "";
data.genera.forEach(i => {
const lang = i.language.name;
if (lang === "en") {
genus = i.genus;
}
});
return genus;
}
setPokemon({
img: data1.sprites.front_default,
name: data1.name,
weight: Math.round(data1.weight / 10),
types: data1.types.map(i => i.type.name),
abilities: data1.abilities.map(i => i.ability.name),
moveList: data1.moves.map(i => i.move.name),
height: data1.height / 10,
description: resolveDescription(data2),
genus: resolveGenus(data2),
chanceToCatch: Math.round((data2.capture_rate * 100) / 255),
evolutionURL: data2.evolution_chain.url,
eggGroups: data2.egg_groups.map(g => g.name)
});
} catch (e) {
console.log(e);
}
};
fetchData();
}, [urlPokemonAPI, urlPokemonSpecies]);

Only last item is added to array inside map function

I'm mapping an array inside a map function and I want to add the id of every element inside the array to a state. I'm facing an issue where just the last item is added to the array even though console log shows that the function iterates to correct number of times.
This is the code I have written
const { evc } = this.props;
evc.chargingStationGroups && evc.chargingStationGroups.map((item, key) => {
item.stations.map((stationItem, key) => {
console.log("stationID ",stationItem.stationID);
var stationId = {};
stationId = {
stationId: stationItem.stationID
}
var allIdArray = this.state.stationIdArray.concat(stationId);
this.setState({ stationIdArray: allIdArray })
})
})
Here evc.chargingStationGroups is something like this
[
{
groupId: "16",
groupName: "Sia",
stations: [{stationId: "8", name: "Test"},{stationId: "9", name: "Test2"},{stationId: "10", name: "Test3"}]
},
{
groupId: "17",
groupName: "Maroon5",
stations: [{stationId: "10", name: "Test"},{stationId: "11", name: "Test2"},{stationId: "10", name: "Test3"}]
}
],
How can i add all stationItem.stationID to my array, not just the last one.
Only call setState once inside all your rendering (because setState is asynchronous)
Assuming you don't have dupes of station between chargingStationGroups, just concat everybody
const { evc } = this.props;
if (evc.chargingStationGroups) {
const ids = evc.chargingStationGroups.flatMap((item, key) => {
return item.stations.map((stationItem, key) => {
return {
stationId: stationItem.stationID
}
})
})
const stationIdArray = this.state.stationIdArray.concat(ids)
this.setState({ stationIdArray })
})
Else just avoid the dupes...
const { evc } = this.props;
if (evc.chargingStationGroups) {
const ids = evc.chargingStationGroups.flatMap((item, key) => {
return item.stations.map((stationItem, key) => {
return {
stationId: stationItem.stationID
}
})
})
const arr = this.state.stationIdArray.concat(ids)
const s = new Set(arr.map(x => x.stationID))
const stationIdArray = [...s].map(stationId => ({ stationId }))
this.setState({ stationIdArray })
})
Not tested because no minimal reproducible example given, but you get the idea...
Original answer: What happens when using this.setState multiple times in React component?
In brief, this.setState is batched and called only once at the end of the loop, leaving this.state.stationIdArray empty all the time. Hence only the result at the final iteration of this statement is kept:
var allIdArray = this.state.stationIdArray.concat(stationId);
Avoid calling setState multiple time in this case:
const { evc } = this.props;
if (evc.chargingStationGroups) {
let allIdArray = [];
evc.chargingStationGroups.forEach(item => {
allIdArray = [
...allIdArray,
...item.stations.map(stationItem => ({
stationId: stationItem.stationId
}))
];
});
this.setState({ stationIdArray: allIdArray });
}
A simple example: https://codesandbox.io/s/bold-swartz-leto5
You should just use forEach if you want to do operations during iteration.
const { evc } = this.props;
evc.chargingStationGroups && evc.chargingStationGroups.forEach((item, key) => {
item.stations.forEach((stationItem, key) => {
console.log("stationID ",stationItem.stationID);
var stationId = {};
stationId = {
stationId: stationItem.stationID
}
var allIdArray = this.state.stationIdArray.concat(stationId);
this.setState({ stationIdArray: allIdArray })
})
})

Sequelize CreateOrUpdate Function

I am using node version v10.15.3 and "sequelize": "^4.22.8". When using bulkCreate I am getting double values in my db:
My model looks like the following:
module.exports = (sequelize, DataTypes) => {
const Company = sequelize.define('Company', {
name: DataTypes.STRING,
url: DataTypes.STRING,
symbol: DataTypes.STRING,
description: DataTypes.STRING,
}, {})
Company.associate = function(models) {
Company.hasMany(models.Rating)
};
return Company
};
I have created a custom createOrUpdateCompany() function. Find below my minimum executable example:
const models = require('./src/models');
const Company = require('./src/models').Company;
async function getAllCompanies() {
try {
let res = await Company.findAll()
return res;
} catch (error) {
console.log(error)
}
}
async function createOrUpdateCompany(dataArray) {
if (dataArray === undefined || dataArray.length === 0) return ''
let iss = []
const allCompanies = await getAllCompanies()
// flatten array
dataArray = [].concat.apply([], dataArray)
if (allCompanies !== undefined) {
// 1. remove exact dedupes from dataArray
dataArray = [...new Map(dataArray.map(obj => [JSON.stringify(obj), obj])).values()]
// 2. compare dataArray to allCompanies and remove difference
// dataArray = dataArray.filter(cv => !allCompanies.find(e => e.symbol === cv.symbol))
dataArray = dataArray.filter(cv => !allCompanies.find(e => e.symbol === cv.symbol))
// 3. Remove null values for link and "" values for name
dataArray = dataArray.filter(cv => !(cv.name === '' || cv.url === null))
}
try {
iss = await Company.bulkCreate(dataArray, {
fields: [
'name',
'url',
'symbol',
'description',
]
})
} catch (error) {
console.log(error)
}
return iss
}
let data = [{
"date": "9/14/2019",
"issuer": "Issuer6",
"name": "Name1",
"symbol": "Symbol2",
"url": "www.url.com"
}, {
"date": "9/11/2029",
"issuer": "Issuer3",
"name": "Name1",
"symbol": "Symbol1",
"url": "www.url.com"
}, {
"date": "8/13/2019",
"issuer": "Issuer1",
"name": "Name1",
"symbol": "Symbol1",
"url": "www.url.com"
}]
async function main() {
// setup db
await models.sequelize.sync({
force: true
})
await createOrUpdateCompany(data)
await createOrUpdateCompany(data)
await createOrUpdateCompany(data)
await createOrUpdateCompany(data)
console.log("##################### DONE #####################");
}
main()
When executing the above example I get the following in my db:
However, I would like to get only the following as result:
As you can see I only get two entries based on the unique result.
Any suggestions why my createOrUpdateCompany() is wrong?
I appreciate your replies!
There are 2 ways to achieve uniqueness in your result
Filter your array data before execution (Javascript stuff)
Unique elements from array
Make specific fields unique in the DB (composite unique) (Database stuff)Add Composite unique key in MySQL

Categories

Resources