Delay Mocha test programmatically - javascript

I'm familiar with Mocha's support of delaying the root suite to enable performing asynchronous operations before a test is executed, by use of running Mocha with the --delay flag, but this impacts all tests.
Is it possible to do something like this on a test by test basis, without using the --delay flag?
An example of a working asynchronous test is below, but unfortunately not of all of our tests are async and call run(). Please note that the below example also leverages dynamically generating tests to generate a test for each URL that is detected during the asynchronous operation before the suite executes.
driver = await new Builder().forBrowser('chrome').build();
await driver.get('http://example.org');
await driver.findElements(By.css('article header a')).then(async function (anchors) {
Promise.all(
anchors.map(async anchor => {
return new Promise(async function (resolve, reject) {
try {
const href = await anchor.getAttribute('href');
urls.push(href);
resolve();
} catch (err) {
console.log('Catch')
reject(err);
}
})
})
).then(function () {
driver.quit();
describe('my suite', function () {
urls.forEach(function (url) {
it(`Loads ${url}`, async function () {
await driver.get(url);
await driver.getTitle().then(function (title) {
assert.strictEqual(1, 1);
});
});
});
});
run();
})
});
Following #destroyer's suggestion, I was successful in accomplishing something similar using asynchronous hooks (below), but cannot dynamically generate a separate test for each URL since Mocha does not delay the root suite execution.
describe('Async test suite', function () {
const getAnchors = function () {
return new Promise(async (resolve) => {
driver = await new Builder().forBrowser('chrome').build();
await driver.get('http://example.org');
await driver.findElements(By.css('article header a'))
.then(async (anchors) => {
resolve(anchors);
})
});
}
const getUrls = function (anchors) {
return new Promise(async resolve => {
for (i = 0; i < anchors.length; i++) {
urls.push(await anchors[i].getAttribute('href'));
if (i === (anchors.length - 1)) {
resolve(urls);
}
}
});
}
const iterateUrls = function (urls) {
return new Promise(async (resolve, reject) => {
for (i = 0; i < urls.length; i++) {
await driver.get(urls[i])
const thisUrl = await driver.getCurrentUrl();
try {
await assert.strictEqual(thisUrl, urls[i]);
} catch (err) {
reject(err);
break;
}
if (i === (urls.length - 1)) {
resolve();
}
}
})
}
async function asyncController() {
Promise.all([
anchors = await getAnchors(),
await getUrls(anchors)
])
}
// Trigger async functions here
before(function (done) {
asyncController()
.then(() => {
done();
})
});
// Close the browser after test completes
after(async function () {
await driver.quit()
});
describe('Checks URLs', function () {
it('Iterates over URLs', async function (done) {
try {
await iterateUrls(urls);
} catch (err) {
done(err);
}
});
});
});

Related

Retry functionality in promise chain

i have a promise chain
If i receive error in getServiceCost I want to repeat the chain again (retry) for 2 times how can i achieve this when using Promise chain , meaning again execute getUser, getServiceCost
getUser(100)
.then(getServices)
.then(getServiceCost)
.then(console.log);
function getUser(userId) {
return new Promise((resolve, reject) => {
console.log('Get the user from the database.');
setTimeout(() => {
resolve({
userId: userId,
username: 'admin'
});
}, 1000);
})
}
function getServices(user) {
return new Promise((resolve, reject) => {
console.log(`Get the services of ${user.username} from the API.`);
setTimeout(() => {
resolve(['Email', 'VPN', 'CDN']);
}, 3 * 1000);
});
}
function getServiceCost(services) {
return new Promise((resolve, reject) => {
console.log(`Calculate the service cost of ${services}.`);
setTimeout(() => {
resolve(services.length * 100);
}, 2 * 1000);
});
}
If i receive error in getServiceCost I want to repeat the chain again (retry) for 2 times how can i achieve this when using Promise chain , meaning again execute
getUser, getServiceCost
I'd use an async function (all modern environments support them, and you can transpile for obsolete environments), which lets you use a simple loop. Perhaps as a utility function you can reuse:
async function callWithRetry(fn, retries = 3) {
while (retries-- > 0) {
try {
return await fn();
} catch (error) {
if (retries === 0) {
throw error;
}
}
}
return new Error(`Out of retries`); // Probably using an `Error` subclass
}
Using it:
callWithRetry(() => getUser(100).then(getServices).then(getServiceCost))
.then(console.log)
.catch(error => { /*...handle/report error...*/ });
Or
callWithRetry(async () => {
const user = await getUser(100);
const services = await getServices(user);
return await getServiceCost(services);
})
.then(console.log)
.catch(error => { /*...handle/report error...*/ });

Wait for async function to end

I have the following function that works fine, except I need to wait until it finishes to execute the next statement:
zohoAuth.zoho_oAuth = function () {
// return new Promise((resolve, reject) => {
zohoAuth.state = utils.uuid();
const url = zohoAuth.authorizationURL();
zohoAuth.popUp(url);
getAuthCodeFromCatalyst();
//setTimeout(getAuthCodeFromCatalyst,1000);
function getAuthCodeFromCatalyst() {
return new Promise(function (resolve, reject) {
(async function waitForFoo() {
const gotAuthState = await zohoAuth.getUserDataFromStorageState(zohoAuth.state)
await gotAuthState;
if (gotAuthState) return resolve();
setTimeout(waitForFoo, 1000);
})();
});
}
console.log("bottom of zoho auth")
return true;
// });
}
I call the function with this:
zohoAuth.zoho_oAuth();
console.log("done waiting");
How do i wait for this to finish?
You're making this harder on yourself. Make sure to avoid the explicit promise constructor anti-pattern -
zohoAuth.zoho_oAuth = function () {
zohoAuth.state = utils.uuid();
const url = zohoAuth.authorizationURL();
zohoAuth.popUp(url);
return zohoAuth.getUserDataFromStorageState(zohoAuth.state);
}
You can access the result by attaching a .then handler to the result of your function call -
zohoAuth.zoho_oAuth()
.then(authState => console.log("bottom of auth state", authState))
.catch(console.error)
If you want to use async and await, go ahead. If an error occurs, don't catch it. Instead allow it to bubble up and be handled by the caller -
async function doAuth (...) {
const authState = await zohoAuth.zoho_oAuth()
console.log("received auth state", authState)
return "done" // or whatever
})
doAuth().then(console.log, console.error)
You should consider awaiting on the promise. Below snippet shows the difference of using await -
const asyncFunction = function() {
return new Promise(function(resolve, reject) {
setTimeout(() => {
console.log('inside promise');
resolve();
}, 100);
});
}
function callWithoutAwait() {
asyncFunction();
console.log('after without await function');
}
callWithoutAwait();
async function callWithAwait() {
await asyncFunction();
console.log('after with await function');
}
callWithAwait();
I was able to accomplish what I needed below is the code. Thanks for the help!
zohoAuth.zoho_oAuth = function() {
zohoAuth.state = utils.uuid();
const url = zohoAuth.authorizationURL();
zohoAuth.popUp(url);
return new Promise(function (resolve, reject) {
(async function waitForFoo() {
const gotAuthState = await zohoAuth.getUserDataFromStorageState(zohoAuth.state)
await gotAuthState;
if (gotAuthState) return resolve();
setTimeout(waitForFoo, 1000);
})();
});
}
And this is the call:
zohoAuth.zoho_oAuth()
.then(authState => console.log("bottom of auth state", authState))
.catch(console.error)

Async / Await JavaScript issue

shortly, I was trying to simulate async / await behavior in JavaScript but getting not expected
const urls = ['api1', 'api2', 'api3']
async function start() {
for (i = 0; i < urls.length; i++) {
result = await getFromApi(urls[i])
console.log(result)
}
}
async function getFromApi(apiUrl) {
return await new Promise((resolve, reject) => {
resolve(apiUrl)
}).then(apiUrl => apiUrl)
}
console.log('start ....')
start()
console.log('done ... ')
so the expected result should be
start ....
api1
api2
api3
done ...
but I am getting
start ....
done ...
api1
api2
api3
The function called start() needs be used with await. Also in the same time your code needs to be wrapped with async function.
Try as the following:
(async () => {
const urls = ['api1', 'api2', 'api3']
async function start() {
for (i = 0; i < urls.length; i++) {
result = await getFromApi(urls[i])
console.log(result)
}
}
async function getFromApi(apiUrl) {
return await new Promise((resolve, reject) => {
resolve(apiUrl)
}).then(apiUrl => apiUrl)
}
console.log('start ....')
await start()
console.log('done ... ')
})();
I hope this helps!
start() isn't being awaited. If this is at the top-level scope then you would probably use .then() on the returned Promise object. For example:
console.log('start ....');
start().then(() => {
console.log('done ... ');
});

Passing an error from one async function to another

I have a convoluted system, which totally works on async/await. What I want is to handle multiple types of errors from an async function in one and only try/catch block. Which means that I call this function from another async function.
But the concept of handling exceptions in a parent async function seems to fail. In the below example what I get - is just a warning about unhandled promise rejection, and the catch block in the parent won't ever get an error. I've tried this also with simply throwing and error, but unsuccessfully either.
const die = (word) => new Promise((resolve, reject) => reject(word));
const live = () => new Promise((resolve, reject) => resolve(true));
const daughterAsync = async () => {
await live();
try {
await die('bye');
} catch (err) {
return Promise.reject(err);
}
try {
await die('have a beatiful time');
} catch (err) {
return Promise.reject(err);
}
await live();
};
const parentAsync = async () => {
try {
daughterAsync();
} catch(err) {
console.log('error catched'); // never happens
console.log(err);
}
};
parentAsync();
I have a feeling that I don't get something about async functions to perform such a stunt
Your daughterAsync(); line only starts the promise running, but it doesn't save the reference to it or wait for it to resolve. You need to await the promise returned by daughterAsync inside of parentAsync's try block in order to catch errors in daughterAsync:
const die = (word) => new Promise((resolve, reject) => reject(word));
const live = () => new Promise((resolve, reject) => resolve(true));
const daughterAsync = async () => {
await live();
try {
await die('bye');
} catch (err) {
return Promise.reject(err);
}
try {
await die('have a beatiful time');
} catch (err) {
return Promise.reject(err);
}
await live();
};
const parentAsync = async () => {
try {
await daughterAsync();
} catch(err) {
console.log('error catched');
console.log(err);
}
};
parentAsync();

nodejs how to use multiple await promises

how can i use multi promise await in my codes ? when i wanna use second await for second promise it throw an error
function ReadJSONFile() {
return new Promise((resolve, reject) => {
fs.readFile('import.json', 'utf-8', (err, data) => {
if (err) reject(err);
resolve(JSON.parse(data));
});
});
}
const Get_Image = async (Path) => {
Child_Process = exec('node get_image.js "'+Path+'",(err,stdout,stderr) =>
return new Promise((resolve,reject) => {
resolve(stdout);
});
}
const Catch = async () => {
let get_json_file = await ReadJSONFile(); // this works perefectly
for(var i=0;i< Object.keys(get_json_file);i++) {
console.log(await Get_Image(get_json_file[i].image_path); //but this throw error
}
}
you didn`t return a promise that is why you got an error
const Get_Image = async (Path) => {
return new Promise((resolve,reject) => {
Child_Process = exec('node get_image.js "'+Path+'",(err,stdout,stderr) =>
resolve(stdout);
});
});
}

Categories

Resources