Cucumber Puppeteer: Problem passing argument into arrow function - cucumber

This is from a world.js in a cucumber puppeteer project.
The first code block causes an error. But if I hard code the selector as in the second code block, there is no error. How do I pass the argument into the arrow function so I don't have to hard code the selector? TIA
Hard coded selector: works
async getOriginalForecastDate(selectorTitle, selectorDate, wait = 0) {
await this.page.waitForSelector(selectorTitle);
await this.page.waitForSelector(selectorDate);
const originalDateStr = await this.page.evaluate(selectorDate => {
let result = document.querySelector('div[class="rollmodel_cal_date"]');
////let result = document.querySelector(selectorDate);
return result.innerText.trim();
});
const originalDate = utils.constructDate(originalDateStr);
return originalDate;
}
Trying to pass selector as an argument: doesn't work
async getOriginalForecastDate(selectorTitle, selectorDate, wait = 0) {
await this.page.waitForSelector(selectorTitle);
await this.page.waitForSelector(selectorDate);
const originalDateStr = await this.page.evaluate(selectorDate => {
/////let result = document.querySelector('div[class="rollmodel_cal_date"]');
let result = document.querySelector(selectorDate);
return result.innerText.trim();
});
const originalDate = utils.constructDate(originalDateStr);
return originalDate;
}

You should pass arguments after evaluate arrow function. like this page.evaluate(pageFunction, ...pageFunction arguments). docs.
async getOriginalForecastDate(selectorTitle, selectorDate, wait = 0) {
await this.page.waitForSelector(selectorTitle);
await this.page.waitForSelector(selectorDate);
const originalDateStr = await this.page.evaluate(
(selectorDate, arg1, arg2) => {
/////let result = document.querySelector('div[class="rollmodel_cal_date"]');
let result = document.querySelector(selectorDate);
return result.innerText.trim();
},
selectorDate,
arg1,
arg2,
);
const originalDate = utils.constructDate(originalDateStr);
return originalDate;
}

Related

Facing issue with scooping in node JS

This code is showing empty object ( {} )
// declared at top
let mainData = {};
let trainStations = {};
let routes = {};
let trainNo = {};
data["data"].forEach(async (element) => {
const response2 = await fetch(
`https://india-rail.herokuapp.com/trains/getRoute?trainNo=${element["train_base"]["train_no"]}`
);
const data2 = await response2.json();
data2["data"].forEach((ele) => {
routes[ele["source_stn_code"]] = true;
});
trainNo[element["train_base"]["train_no"]] = routes;
});
console.log(trainNo);
if i do this then i will give response with data
data["data"].forEach(async (element) => {
const response2 = await fetch(
`https://india-rail.herokuapp.com/trains/getRoute?trainNo=${element["train_base"]["train_no"]}`
);
const data2 = await response2.json();
data2["data"].forEach((ele) => {
routes[ele["source_stn_code"]] = true;
});
trainNo[element["train_base"]["train_no"]] = routes;
console.log(trainNo);
});
maybe there is some scooping issue please kindly help me to solve this problem :)
Please refer here and also check this.
As a short note, using await inside a forEach() loop will give unexpected results. This is because the forEach() does not wait until the promise to settled (either fulfilled or rejected).
A simple solution for this could be using either the traditional for loop or the for..of loop.
for(let element of data["data"]){
const response2 = await fetch(
`https://india-rail.herokuapp.com/trains/getRoute?trainNo=${element["train_base"]["train_no"]}`
);
const data2 = await response2.json();
data2["data"].forEach((ele) => {
routes[ele["source_stn_code"]] = true;
});
trainNo[element["train_base"]["train_no"]] = routes;
}
console.log(trainNo);
NOTE: Make sure to wrap the above for..of loop inside an async function because the await keyword is allowed inside a function only when the function is defined with async keyword.

How to return data in foreach loop having multiple async functions inside

I am calling this function using await and store the data in the result variable. But the function returns without completing the foreach loop. How can I make this function return only if the foreach loop ends?
let result = await prepareStocks(data);
async function prepareStocks(incomingStocks) {
var stockCodes = incomingStocks.stocks.split(',');
var stockPrices = incomingStocks.trigger_prices.split(',');
var alertName = incomingStocks.alert_name;
stockCodes.forEach(async(stocks, index) => {
if (stockPrices[index] > 100) {
var stockCodes = {
code: stocks,
price: stockPrices[index],
orderType: (urls.buy.includes(alertName) ? 'BUY' : 'WATCH'),
target: await setSellPrice(stockPrices[index], 1),
stopLoss: await setStopLoss(stockPrices[index], 1),
}
STOCKS.push(stockCodes);
}
});
return STOCKS;
}
Try using Promise.all
Something like this =>
let result = await prepareStocks(data);
async function prepareStocks(incomingStocks) {
var stockCodes = incomingStocks.stocks.split(',');
var stockPrices = incomingStocks.trigger_prices.split(',');
var alertName = incomingStocks.alert_name;
const STOCKS = await Promise.all(stockCodes.map(async (stocks, index) => {
if (stockPrices[index] > 100) {
var stockCodes = {
code: stocks,
price: stockPrices[index],
orderType: (urls.buy.includes(alertName) ? 'BUY' : 'WATCH'),
target: await setSellPrice(stockPrices[index], 1),
stopLoss: await setStopLoss(stockPrices[index], 1),
}
return stockCodes;
}
})
return STOCKS;
}
After reading the async/await part, I was wondering there is nothing wrong with the way how async/await is used & STOCKS must be returned with expected values. So, I setup a simulation as follows & ran the code.
async function setSellPrice(price) { return new Promise(function resolver(resolve) {setTimeout(function someTimeLater() { resolve(price)});}); }
async function setStopLoss(price) { return new Promise(function resolver(resolve) {setTimeout(function someTimeLater() { resolve(price)});}); }
const data = { stocks: 'A,B,C'. trigger_prices: '100,120,140', alert_name: 'XYZ' };
let result = await prepareStocks(data);
And got STOCKS not defined error.
Your code was correct in terms of how async/await works. The problem with the original code was, the STOCKS variable was not defined. I am assuming, it was not defined anywhere else. So, doing the following works as expected.
let result = await prepareStocks(data);
async function prepareStocks(incomingStocks) {
const STOCKS = [];
var stockCodes = incomingStocks.stocks.split(',');
var stockPrices = incomingStocks.trigger_prices.split(',');
var alertName = incomingStocks.alert_name;
stockCodes.forEach(async(stocks, index) => {
if (stockPrices[index] > 100) {
var stockCodes = {
code: stocks,
price: stockPrices[index],
orderType: (urls.buy.includes(alertName) ? 'BUY' : 'WATCH'),
target: await setSellPrice(stockPrices[index], 1),
stopLoss: await setStopLoss(stockPrices[index], 1),
}
STOCKS.push(stockCodes);
}
});
return STOCKS;
}

Async function returning empty value then doing operations?

Hey I have written this code in node js
async function getUdemydata(){
try{
var finalData = []
const res = await axios.get(baseUrl)
const $ = cheerio.load(res.data);
$('div[class="content"]').each(async (i,el) =>{
const courseTitle = $(el).find("a").text()
const aa = $(el).html()
const courseUrl = await getCoupenCode($(el).find("a").attr("href"))
const courseDescription = $(el).find(".description").text().trim()
const courseImage = await formateImageUrl($(el).find(".image").html())
var dataObj = {
"title": courseTitle,
"description": courseDescription,
"image": courseImage,
"link": courseUrl
}
finalData.push(dataObj);
console.log('appended');
})
return (finalData);
} catch(error){
console.error(error);
}
}
(async()=>{
var rs = await getUdemydata();
console.log(rs);
})()
When I call the getUdemydata() function only the empty array is printed after that appended which is inside the function is being printed what should I change in the code so that the function will return the final array
The definition of .each is as follows:
each<T>(fn: (i: number, el: T) => boolean | void): Cheerio<T>
It is not Promise-aware, so does not await the async function you supply as its parameter.
Remember that an async function is just a normal function that returns a Promise, so you could map these function calls, ending up with an array of Promise, then wait for them all.
// var finalArray = [] // <-- NO
const promises = $('div[class="content"]')
.toArray()
.map(async (el,i) => {
// same body as `each` in your code
// except, don't push into `finalArray`
// just return your value
// finalData.push(dataObj); // <-- NO
return dataObj;
});
const finalArray = await Promise.all(promises);
or if the parallelism of running all those promises at once is too much, then the documentation suggests that a Cheerio is Iterable, so just loop over $('div[class="content"]') with a normal
for(const el of $('div[class="content"]')) { //...

why my data returning the state before get updated in async function nodejs typescript

i dont know why variable result in console log is returning the state before get updated in this line
result[msisdn] = "customer exist"
the code itself it executed but why keep returning the state before get modified, i think i already give await keyword on all async code that i write
code.ts
async migrateCustomerImageUseCase(request: ListCustomerForMigration, response: Response): Promise<any>{
const result = {} as any
const lists = request as any
await lists.forEach( async (element: any[]) => {
const msisdn = element[0] as string
const isCustomerExist = await this.ekycHandler.isCustomerExist(msisdn)
if(isCustomerExist){
result[msisdn] = "customer exist" as string
this.logger.info(`${msisdn} = ${result[msisdn]}`);
// tslint:disable-next-line:no-console
// console.log(result[msisdn])
// const customerImageData:CustomerImageData = {
// customerIdCardImage: element[2],
// customerImage: element[3],
// msisdn: element[0] as string
// };
// const localFileName = await this.uploadBase64ImageToLocalUseCase(customerImageData, response)
// const ossFileName = await this.uploadImageToOssUseCase(localFileName, response)
// ossFileName["customerId"] = isCustomerExist.id
// await this.ekycPortalRepo.createOrUpdateCustomerDetailCifFromMigrationData(ossFileName)
} else {
result[msisdn] = "customer not exist" as string
this.logger.info(`${msisdn} = ${result[msisdn]}`);
}
})
return result
}
and the code.ts is called in another async function with this line
const migrateResult = await this.ekycPortalUseCase.migrateCustomerImageUseCase(listCustomers, response)
I think your case is mostly connected with this - Using async/await with a forEach loop
You cannot use forEach indeed. Each of the async callback function calls does return a promise, but you're throwing them away instead of awaiting them. Just use map instead, and you can await the array of promises that you'll get with Promise.all
You should do something like that:
const promises = lists.map(() => {
return (async () => {
const msisdn = element[0] as string
const isCustomerExist = await this.ekycHandler.isCustomerExist(msisdn)
if(isCustomerExist){
result[msisdn] = "customer exist" as string
this.logger.info(`${msisdn} = ${result[msisdn]}`);
} else {
result[msisdn] = "customer not exist" as string
this.logger.info(`${msisdn} = ${result[msisdn]}`);
}
})()
})
await Promise.all(promises)
Typescript doesn't wait for a forEach to be fully completed before moving on, that's why the return result statement gets executed before it is filled with the results from inside the loop.
One solution might be to change from using forEach to a basic for loop to avoid this issue entirely.

async function doesn't wait of inside await in nodejs

I am implementing function monthlyRevenue.
Simply, it will return total monthly revenue,and it takes arguments of station array which will make revenues, month and year.
Problem
Inside of this function I have getStationPortion which will fetch the revenue portion of user's.
So I would like to make it return object like this.
stationsPortion = {station1 : 30, station2 : 20}
In the monthlyRevenue
const stationPortions = await getStationPortions(stations)
console.log("portion map", stationPortions //it will be shown very beginning with empty
getStationPortions
const getStationPortions = async (stations) => {
let stationPortions = {}
stations.map(async (value) => {
const doc = await fdb.collection('Stations').doc(value).get()
if (!doc.exists) {
console.log("NO DOC")
} else {
stationPortions[value] = doc.data().salesPortion
console.log(stationPortions) //it will be shown at the last.
}
})
return stationPortions
}
I thought that async function should wait for the result, but it does not.
I am kind of confusing if my understanding is wrong.
Thank you
(by the way, fdb is firebase admin(firestore)
Working code
const getStationPortions = async (stations) => {
let stationPortions = {}
await Promise.all(stations.map(async (value) => {
const doc = await fdb.collection('Stations').doc(value).get()
if (!doc.exists) {
console.log("NO DOC")
} else {
stationPortions[value] = doc.data().salesPortion
console.log(stationPortions)
}
}))
return stationPortions
}
module.exports = router;

Resources