I faced some issues regarding concurrency issue in client side JS. There are two lines of code which sends request using axios. But the following code is run before the callback function for axios request is completed.
Any way to solve this concurrency problem? As far as I know async/await is only used in backend JS like Node.JS
var declaration:
var chartGroundTank = echarts.init(document.getElementById('chart-ground-tank'));
var chartElevatedTank = echarts.init(document.getElementById('chart-elevated-tank'));
var optionChartGroundTank = {
series: {
type: 'liquidFill',
data: [{
name: '',
value: 0,
// waveAnimation: false,
amplitude: '4%',
itemStyle: {
color: '',
},
}]
}
};
var optionChartElevatedTank = {
series: {
type: 'liquidFill',
data: [{
name: "",
value: 0,
itemStyle: {
color: ''
},
// waveAnimation: false,
amplitude: '4%',
}]
}
};
function call:
axios
.get("/api/v1/firstAPI")
.then(function (response) {
const groundTankData = response.data.sensor1;
const elevatedTankData = response.data.sensor2;
setWaterTankColor(groundTankData, optionChartGroundTank, 'gnd'); // the values are
setWaterTankColor(elevatedTankData, optionChartElevatedTank, 'elv'); // set slower
chartGroundTank.setOption(optionChartGroundTank); // this two lines
chartElevatedTank.setOption(optionChartElevatedTank); // will run earlier
....
)};
function definition:
function setWaterTankColor(waterLevel, tankType) {
axios.get("/api/v1/metrics/getData", {
...config,
})
.then((response) => {
var { sensor1Info1, sensor1Info2, sensor2Info1, sensor2Info2 } = response.data;
if (tankType == 'gnd') {
var color = waterLevel < sensor1Info1 / 100 || waterLevel > sensor1Info2 / 100 ? optionChartGroundTank.series.data[0].itemStyle.color = ["red"] : optionChartGroundTank.series.data[0].itemStyle.color = ["#2f529a"];
}
else {
var color = waterLevel < sensor2Info1 / 100 || waterLevel > sensor2Info2 / 100 ? optionChartElevatedTank.series.data[0].itemStyle.color = ["red"] : optionChartElevatedTank.series.data[0].itemStyle.color = ["#2f529a"];
}
});
}
You should return the promise, you have in setWaterTankColor, then you can set the option after the callback completed.
function setWaterTankColor(waterLevel, tankType) {
return axios.get("/api/v1/metrics/getData", {
...config,
})
.then((response) => {
var { sensor1Info1, sensor1Info2, sensor2Info1, sensor2Info2 } = response.data;
if (tankType == 'gnd') {
var color = waterLevel < sensor1Info1 / 100 || waterLevel > sensor1Info2 / 100 ? optionChartGroundTank.series.data[0].itemStyle.color = ["red"] : optionChartGroundTank.series.data[0].itemStyle.color = ["#2f529a"];
}
else {
var color = waterLevel < sensor2Info1 / 100 || waterLevel > sensor2Info2 / 100 ? optionChartElevatedTank.series.data[0].itemStyle.color = ["red"] : optionChartElevatedTank.series.data[0].itemStyle.color = ["#2f529a"];
}
});
}
Then you can use the function like this:
axios
.get("/api/v1/firstAPI")
.then(function (response) {
const groundTankData = response.data.sensor1;
const elevatedTankData = response.data.sensor2;
setWaterTankColor(groundTankData, optionChartGroundTank, 'gnd').then(function() { chartGroundTank.setOption(optionChartGroundTank); });
setWaterTankColor(elevatedTankData, optionChartElevatedTank, 'elv').then(function() { chartElevatedTank.setOption(optionChartElevatedTank); });
....
)};
That two lines of codes run earlier because they are sync functions.
In order to make them run later, you can await for the the function setWaterTankColor first.
You can use async/await in your case. I also add trycatch to catch error from API call.
(async function run() {
try {
const response = await axios.get("/api/v1/firstAPI");
const groundTankData = response.data.sensor1;
const elevatedTankData = response.data.sensor2;
await setWaterTankColor(groundTankData, optionChartGroundTank, "gnd");
await setWaterTankColor(elevatedTankData, optionChartElevatedTank, "elv");
chartGroundTank.setOption(optionChartGroundTank);
chartElevatedTank.setOption(optionChartElevatedTank);
} catch (err) {
console.log(err);
}
})();
async function setWaterTankColor(waterLevel, tankType) {
try {
const response = axios.get("/api/v1/metrics/getData", {
...config,
});
const { sensor1Info1, sensor1Info2, sensor2Info1, sensor2Info2 } =
response.data;
if (tankType == "gnd") {
var color =
waterLevel < sensor1Info1 / 100 || waterLevel > sensor1Info2 / 100
? (optionChartGroundTank.series.data[0].itemStyle.color = ["red"])
: (optionChartGroundTank.series.data[0].itemStyle.color = [
"#2f529a",
]);
} else {
var color =
waterLevel < sensor2Info1 / 100 || waterLevel > sensor2Info2 / 100
? (optionChartElevatedTank.series.data[0].itemStyle.color = ["red"])
: (optionChartElevatedTank.series.data[0].itemStyle.color = [
"#2f529a",
]);
}
} catch (err) {
throw err;
}
}
Related
For some reason I have variables outside of my function and I'm updating that variable in my function but when I call that variable in another function I get a undefined typeError
let bikeShare = []
let stations = []
function startRide(vin) {
bikeShare = bikeShare.map((bike) => {
bike.vin === vin ? { ...bike, checkOut: true } : bike
})
return {}
}
function endRide(vin) {
console.log(bikeShare)
bikeShare = bikeShare.map((bike) => {
bike.vin === vin && bike.checkOut
? { ...bike, checkOut: false, totalRides: bike.totalRides + 1 }
: bike
})
return {}
}
function createBike(color = 'red') {
const vin = bikeShare.length + Date.now();
const payload = { vin, color, checkOut: false, totalRides: 0 }
bikeShare.push(payload);
return payload
}
const bike_1 = createBike('red')
const bike_2 = createBike('blue')
const bike_7 = createBike('green')
startRide(bike_1.vin) // in the startRide function I get an array [undefined, undefined, undefined]
endRide(bike_1.vin)
You are in the startRide() function not returning the result of each assignment in the .map method, so it returns undefined which why you see the array of undefined values.
This should fix it:
let bikeShare = []
let stations = []
function startRide(vin) {
bikeShare = bikeShare.map((bike) => {
return bike.vin === vin ? { ...bike, checkOut: true } : bike
})
return {}
}
function endRide(vin) {
console.log(bikeShare)
bikeShare = bikeShare.map((bike) => {
bike.vin === vin && bike.checkOut
? { ...bike, checkOut: false, totalRides: bike.totalRides + 1 }
: bike
})
return {}
}
function createBike(color = 'red') {
const vin = bikeShare.length + Date.now();
const payload = { vin, color, checkOut: false, totalRides: 0 }
bikeShare.push(payload);
return payload
}
const bike_1 = createBike('red')
const bike_2 = createBike('blue');
const bike_7 = createBike('green');
startRide(bike_1.vin) // in the startRide function I get an array [undefined, undefined, undefined]
endRide(bike_1.vin)
To lift this out of comment, the body of the map argument function in startRide is enclosed in curly braces. You could remove the braces or put return bike inside the braces to stop it returning undefined.
However, setting bike.vin to a bike "payload" object with checkout set to true, leaving bike.checkout set to false, is a bug. One solution might be to use find instead of map:
let bikeShare = []
let stations = []
function startRide(vin, start = true) {
const bike = bikeShare.find(bike=>bike.vin === vin);
if( bike) {
bike.checkOut = start;
}
return bike; // for debugging
}
function endRide(vin) {
return startRide( vin, false);
}
function createBike(color = 'red') {
const vin = bikeShare.length + Date.now();
const payload = { vin, color, checkOut: false, totalRides: 0 }
bikeShare.push(payload);
return payload
}
const bike_1 = createBike('red')
const bike_2 = createBike('blue')
const bike_7 = createBike('green')
console.log( startRide(bike_1.vin));
console.log( endRide(bike_1.vin));
let response = {};
var filters = {
topfeaturedandotherfields: req.body.topfeaturedandotherfields,
};
if (req.body.minprice && req.body.maxprice && req.body.brandName) {
var filters = {
$and: [
{ brandName: { $in: req.body.brandName } },
{ topfeaturedandotherfields: req.body.topfeaturedandotherfields },
{ salePrice: { $gte: req.body.minprice, $lte: req.body.maxprice } },
],
};
var result = await productService.getAllProductofhomepage(
filters,
req.body.ordername,
req.body.orderby
);
} else {
if (req.body.minprice && req.body.maxprice) {
var filters = {
$and: [
{ topfeaturedandotherfields: req.body.topfeaturedandotherfields },
{ salePrice: { $gte: req.body.minprice, $lte: req.body.maxprice } },
],
};
var result = await productService.getAllProductofhomepage(
filters,
req.body.ordername,
req.body.orderby
);
}
if (req.body.brandName) {
var filters = {
$and: [
{ brandName: { $in: req.body.brandName } },
{ topfeaturedandotherfields: req.body.topfeaturedandotherfields },
],
};
var result = await productService.getAllProductofhomepage(
filters,
req.body.ordername,
req.body.orderby
);
}
}
if (req.body.limit == true)
var result = await productService.getAllProductofhomepagewithlimit(filters);
else if (req.body.minprice || req.body.maxprice || req.body.brandName) {
} else {
var result = await productService.getAllProductofhomepage(
filters,
req.body.ordername,
req.body.orderby
);
}
if (result.length > 0) {
response = {
message: "Home page products successfully retrieved",
error: false,
data: result,
};
} else {
response = {
message: "Faild to get products",
error: true,
data: {},
};
}
res.status(200).json(response);
This code is used to filter like to see top feature and bestseller or min and max price and the brand name also in this code sort by order name which could be price or brand name or category also in ascending and descending order so now you can see this code is like if and else but I want to optimize and reduce code
You can make this query quite a lot nicer by just dynamically building the query condition instead of breaking the logic into if/else blocks:
export async function login(req: Request, res: Response): Promise<void> {
const response = {};
let filters = {
topfeaturedandotherfields: req.body.topfeaturedandotherfields,
};
if (req.body.minprice || req.body.maxprice) {
const saleCond = { };
if (req.body.minprice) {
saleCond.$gte = req.body.minprice;
}
if (req.body.maxprice) {
saleCond.$lte = req.body.maxprice;
}
filters.salePrice = saleCond
}
if (req.body.brandName) {
filters.brandName = {$in: req.body.brandName}
}
let result = [];
if (req.body.limit == true) {
result = await productService.getAllProductofhomepagewithlimit(filters)
} else {
result = await productService.getAllProductofhomepage(filters, req.body.ordername, req.body.orderby);
}
res.status(200).json({
message: result.length ? 'Home page products successfully retrieved' : 'Failed to get products',
error: result.length === 0,
data: result,
});
}
Not only is this much clearer we only removed a redundant DB call that was made in the process.
I have a file index.ts which has below two functions in it.
index.ts-
import { parseExpression } from 'cron-parser';
const previousMillisecondTimestamp = (cron: string, endTime: number): number => {
try {
return parseExpression(cron, {
endDate: new Date(endTime),
currentDate: new Date(endTime),
utc: true
}).prev().getTime()
} catch(e) {
return 0;
}
}
const setNextRun = (nextRunTimestampMilliseconds: number, startTimestampMilliseconds: number = new Date().getTime()) => (x: JobScheduleDto): JobScheduleDto[] => {
const scheduleIterator = parseExpression(x.cron, {
/* Line1 */
startDate: new Date(x.last_run > 900 ? x.last_run - 900 : startTimestampMilliseconds),
utc: true
});
let schedule = scheduleIterator.next();
let jobQueue: JobScheduleDto[] = [
{
...x,
next_run: x.next_run ? x.next_run : startTimestampMilliseconds
}
];
for (; schedule.getTime() < nextRunTimestampMilliseconds ; schedule = scheduleIterator.next()) {
if(x.next_run === schedule.getTime() || (!x.next_run && startTimestampMilliseconds === schedule.getTime())) {
continue;
}
jobQueue = [...jobQueue, {
...x,
last_run: previousMillisecondTimestamp(x.cron, schedule.getTime()),
next_run: schedule.getTime()
}];
}
console.log('Values', previousMillisecondTimestamp);
/* Line2 */
const updatedLastRun = previousMillisecondTimestamp(x.cron, schedule.getTime()) || schedule.getTime() || x.last_run;
jobQueue = [...jobQueue, {
...x,
last_run: updatedLastRun,
next_run: schedule.getTime()
}];
return jobQueue;
}
export const testModules = {
setNextRun,
previousMillisecondTimestamp,
}
Both the functions are exported in an object named as testModules (Required only for testing).
Now in order to increase the branch test coverage, I have to cover lines Line1 and Line2 (Commented in above file).
In order to do so in my index.spec.ts file, I want to mock functions previousMillisecondTimestamp and parseExpression(External module cron-parser) in my setNextRun function calls. So I can return the output accordingly (undefined or null) in order to cover all branches.
index.spec.ts-
import { testModules } from "../../src/index";
import { parseExpression } from 'cron-parser';
const currentTime = new Date().getTime();
const nextRunIntervalInMilliseconds = 10 * 60 * 1000;
describe.only('setNextRun function test coverage Test', () => {
it('setNextRun without parameters nextRunTimestampMilliseconds and startTimestampMilliseconds as null and JobScheduleDto object', () => {
const mockedPreviousMillisecondTimestamp = jest.spyOn(testModules, 'previousMillisecondTimestamp').mockImplementationOnce(() => null);
console.log('mockedPreviousMillisecondTimestamp', mockedPreviousMillisecondTimestamp);
const params = { pk: "T5", sk: workdayJobSchedule, last_run: 0, next_run: 0, status: 1, cron: "0 0 4 4 * *", schedule_description: "Desc1" }
try {
testModules.setNextRun(currentTime + nextRunIntervalInMilliseconds)(params);
} catch(error) {
expect(error.message).toBe("Cannot read property 'cron' of undefined");
}
mockedPreviousMillisecondTimestamp.mockRestore();
});
});
So in the above file when I writing below the line I want to mock the functionality for previousMillisecondTimestamp function and to return null.
const mockedPreviousMillisecondTimestamp = jest.spyOn(testModules, 'previousMillisecondTimestamp').mockImplementationOnce(() => null);
Just below that, I am doing a console.log of function to check if the function has been mocked or not and in that console, I am getting that it is mocked which is fine.
But when it goes inside setNextRun call and there I have a console which gives it as a Normal function.
console.log('Values', previousMillisecondTimestamp);
So I have 2 questions -
What is the right way to mock previousMillisecondTimestamp function in my index.spec.ts file?
How I can mock parseExpression function from cron-parser third-party module?
I'm finding myself with some inconvenient to return a certain result inside an async function which has an await request inside. I tried both 3 libraries ("http", "request", "then-request"), and it's always the same.
The main idea is that when the ajax ends, its result should be returned; but instead, it returns undefined (it doesn't respect the async/await).
File: index.server.js
const PyService = require("../../../api/services/PyService/validacionCSV");
module.exports = {
pasajeClientes: async function (matcheos) {
let resultado = await PyService.validate(matcheos);
return resultado;
}
}
File: validacionCSV.js
const request = require('then-request');
module.exports = {
validate: async (matcheos) => {
var response;
await request("GET", `${process.env.API_URL}/validate`, {
json: {
csv: {
clients: "datosPersonas.csv",
products: "movimientos.csv"
},
primary_keys: {
clients: "ID",
products: "ID",
},
branches: {
products: "rama",
},
rules: {
clients: matcheos["clientes"],
products: matcheos["productos"],
}
}
}).done((resultado) => {
let matched = resultado.ok;
let no_relationships = resultado.no_relationships;
let repeated = resultado.repeated;
let total = resultado.total;
let type_errors = resultado.type_errors;
response = {
error: false,
message: "",
errorConTipoDatoClientes: type_errors.clients,
errorConTipoDatoProductos: type_errors.products,
errorConClientesSinProductos: no_relationships.clients,
errorConProductosSinCliente: no_relationships.productos,
errorConClientesRepetidos: repeated.clients,
errorConProductosRepetidos: repeated.products,
cantClientesOk: matched.clients,
cantProductosOk: matched.products,
cantClientesEnArchivo: total.clients,
cantProductosEnArchivo: total.products,
}
if (no_relationships.clients > 0 || no_relationships.products > 0
|| repeated.clients > 0 || repeated.products > 0
|| type_errors.clients > 0 || type_errors.products > 0
) {
response.error = true;
response.message = "Los clientes/productos importados poseen errores."
}
else
response.message = "Los clientes/productos importados no poseen errores."
});
return response;
}
}
You are mixing Promise callbacks with async/await. When working with callbacks you can't define a variable outside and then instantiate within the callback and then try to use it outside the call back again. Read more on Promises.
All I did was return response within the callback function.
Try this
const request = require('then-request');
module.exports = {
validate: async(matcheos) => {
var response;
await request("GET", `${process.env.API_URL}/validate`, {
json: {
csv: {
clients: "datosPersonas.csv",
products: "movimientos.csv"
},
primary_keys: {
clients: "ID",
products: "ID",
},
branches: {
products: "rama",
},
rules: {
clients: matcheos["clientes"],
products: matcheos["productos"],
}
}
}).done((resultado) => {
let matched = resultado.ok;
let no_relationships = resultado.no_relationships;
let repeated = resultado.repeated;
let total = resultado.total;
let type_errors = resultado.type_errors;
response = {
error: false,
message: "",
errorConTipoDatoClientes: type_errors.clients,
errorConTipoDatoProductos: type_errors.products,
errorConClientesSinProductos: no_relationships.clients,
errorConProductosSinCliente: no_relationships.productos,
errorConClientesRepetidos: repeated.clients,
errorConProductosRepetidos: repeated.products,
cantClientesOk: matched.clients,
cantProductosOk: matched.products,
cantClientesEnArchivo: total.clients,
cantProductosEnArchivo: total.products,
}
if (no_relationships.clients > 0 || no_relationships.products > 0 ||
repeated.clients > 0 || repeated.products > 0 ||
type_errors.clients > 0 || type_errors.products > 0
) {
response.error = true;
response.message = "Los clientes/productos importados poseen errores."
} else
response.message = "Los clientes/productos importados no poseen errores."
return response
});
}
}
I have this code, I want to go thru all the links available at the bottom of the page. After clicking them I want to make sure the URL opened is the correct one.
I think the the recursive calls are done too early. Another issue is how can I do to tell that link belongs to certain URL.
function links(browser, total_links) {
if (total_links <= 0) {
browser.end();
return;
}
console.log("Number of links: " + total_links);
console.log('Flag1');
browser
.waitForElementVisible('.bottom .socal>span:nth-child(' + total_links + ')', 1000, function () {
console.log('Flag2');
browser.execute('scrollIntoView(alignToBottom)')
.moveToElement('.bottom .socal>span:nth-child(' + total_links + ')', 3, 3)
.pause(3000)
.click('.bottom .socal>span:nth-child(' + total_links + ') a', function () {
console.log('Flag3');
browser.keys(['\uE006'])
// .assert.urlContains('facebook')
//.assert.urlEquals('https://www.facebook.com/unitel.ao/?fref=ts')
.window_handles(function (result) {
console.log('Flag4');
browser.assert.equal(result.value.length, 2, 'There should be two windows open.');
var handle_1 = result.value[0];
var handle_2 = result.value[1];
browser.switchWindow(handle_2, function () {
browser.closeWindow()
.switchWindow(handle_1, function () {
total_links = total_links - 1;
links(browser, total_links);
});
});
});
console.log('Flag5');
});
console.log('Flag6');
});
}
module.exports = {
'Social links': function (browser) {
var total_links;
browser
.url('http://m.unitel.ao/fit/')
.execute(function () {
return document.querySelectorAll("ul.navbar-nav>li").length;
},
function (tags) {
total_links = tags.value;
links(browser, total_links);
});
// .end();
}
};
Humh, it seems like you were stuck with this days ago.I recommend page-object,it will help you stay away hardcode and easier to change css in the future.
A home page object(home.js) may be like this :
module.exports = {
url: function() {
return 'http://m.unitel.ao/fit/';
},
commands: [{
getUrl: function(n) {
if (n === 3) {
return 'youtube.com/user/tvUNITEL';
}
if (n === 1) {
return 'facebook.com/unitel.ao/?fref=ts';
}
if (n === 2) {
return 'instagram.com/unitelangola/';
}
if (n === 4) {
return 'plus.google.com/110849312028181626033/posts';
}
}
}],
elements: {
facebook: {
selector: '.bottom .socal>span:nth-child(1)',
},
instagram: {
selector: '.bottom .socal>span:nth-child(2)'
},
youtube: {
selector: '.bottom .socal>span:nth-child(3)'
},
googleplus: {
selector: '.bottom .socal>span:nth-child(4)'
}
}
};
And in your test should be like :
module.exports = {
'Social links': function(browser) {
const homePage = browser.page.home();
var j = 0;
for (var i in homePage.elements) {
homePage
.navigate()
.waitForElementVisible(homePage.elements[i].selector, 5000, false,
function() {
browser.pause(3000);
})
.click(homePage.elements[i].selector, function() {
browser
.pause(2000)
.window_handles(function(result) {
url = homePage.getUrl(j + 1);
var home = result.value[0];
var handle = result.value[1];
browser
.switchWindow(handle)
.verify.urlContains(url)
.closeWindow()
.switchWindow(home);
j += 1;
});
})
}
}
};
PS:In case you dont know how to create a page-object, here is the doc http://nightwatchjs.org/guide#using-page-objects.
In config file
Nightwatch.js:
"src_folders" : ["tests"],
"output_folder" : "reports",
"custom_commands_path" : "",
"custom_assertions_path" : "",
"page_objects_path" : "./lib/pages", /* you need to add the path,e.g: './lib/pages', */
"globals_path" : "",