I have a test that awaits an async function, then awaits a timeout, then awaits another async function.
it('runs a snipe successfully', async () => {
const exitCode = await john.chat.sendMoneyInChat(channel.topicName, channel.name, "0.01", botUsername);
console.log('timeout?')
await timeout(3000);
console.log('timeout!');
console.log('running bal check')
let values;
const nbl = await croupier.checkWalletBalance(process.env.CROUPIER_RINGO_USERNAME);
expect(nbl).toEqual(123);
})
Based on my console.log output, the afterAll teardown process begins right after the timeout? log statement. In other words, I don't see "timeout!" in the console log – I see the console.log statements within the afterAll teardown.
What gives?
EDIT:
Thanks to #Metalmi's help, I've fixed one bug. Now my code is:
it('runs a snipe successfully', async () => {
jest.useFakeTimers()
const exitCode = await john.chat.sendMoneyInChat(channel.topicName, channel.name, "0.01", botUsername);
console.log('timeout?')
jest.advanceTimersByTime(20000)
console.log('timeout.');
console.log('running bal check')
let values;
const nbl = await croupier.checkWalletBalance(process.env.CROUPIER_RINGO_USERNAME);
expect(nbl).toEqual(123);
});
Here's the checkWalletBalance function:
public checkWalletBalance(username: string): Promise<any> {
let balance: number = 0;
const self = this;
return new Promise(async (resolve) => {
try {
const acct = await self.bot1.wallet.lookup(username);
console.log("acct", acct);
const balances = await self.bot2.wallet.balances(acct.accountId);
console.log("balances", balances);
balances.forEach((acctDetail) => {
console.log(acctDetail.balance[0].amount);
balance += parseFloat(acctDetail.balance[0].amount);
});
resolve(balance);
} catch (e) {
console.log(e);
throw e;
}
});
}
I am guessing there is some problem of having async functions inside the Promise?
The Jest teardown starts before console.log("acct", acct) happens within checkWalletBalance, so something's still wrong.
To use/test standard timer functions, you need to instruct jest to use fake timers: jest.useFakeTimers(). Then you need to manually advance time: jest.advanceTimersByTime(msToRun)
EDIT:
Issue is that you declared Promise as async, and not the checkWalletBalance() itself. Changing that should fix it.
Also, to be sure about assertion being checked, you can call expect.assertions(1) at the beginning of test, so jest knows that test must have at least one assertion and won't finish before it's checked.
Related
const pdf = require('pdfkit')
const QR = require('qrcode')
const emailTickets = async (userEvent, tickets) => {
await createQRCodes(tickets)
await createTicketPDF(tickets, userEvent)
await sendGridEmail(emailDetails, true)
}
The problem seems to be that async/await isn't working properly for the createTicketPDF function.
When I run the above emailTickets function a blank pdf document is emailed. However, when I run it with the below setTimeOut, the pdf contains all the detail that I want.
const emailTickets = async (userEvent, tickets) => {
await createQRCodes(tickets)
await createTicketPDF(tickets, userEvent)
setTimeout(async() => {await sendGridEmail(emailDetails, true)}, 5000);
}
I would prefer a solution where the code waited for the createTicketPDF function to finish before calling the sendGridEmail function.
Below is the code for the createTicketPDF function:
const createTicketPDF = async (tickets, userEvent) => {
const doc = new pdf
doc.pipe(fs.createWriteStream(`./tickets/${tickets[0]._id}.pdf`))
await tickets.map(async(ticket, index)=> {
return(
await doc.fontSize(30),
await doc.text(userEvent.title),
await doc.fontSize(10),
await doc.moveDown(),
await doc.text(`Venue: ${userEvent.venue}, ${userEvent.address1} `),
await doc.moveDown(),
await doc.fontSize(20),
await doc.text(`Ticket ${index+1} of ${tickets.length}: ${ticket.ticketType}`),
await doc.image(`./qrCodes/${ticket._id}.png`, {
align: 'center',
valign: 'center'
}),
await doc.addPage()
)
})
doc.end()
}
I had a forEach loop in this function but replaced it with a map on learning that forEach loops don't work well with async await.
I have also tried using (const ticket of tickets){} but this didn't work either
VS code is suggesting that none of the awaits within the map do anything
For completeness, below is the createQRCodes. This function is working perfectly.
const createQRCodes = async (tickets) => {
let CreateQRCodes = tickets.map(ticket => {
return(
QR.toFile(`./qrCodes/${ticket._id}.png`, String([ticket._id, ticket.randomNumber, ticket.creationTime.getTime(), ticket.userEvent]))
)})
await Promise.all(CreateQRCodes)
}
Any ideas where I am going wrong
Twilio SendGrid developer evangelist here.
As far as I can tell from the documentation, PDFKit is a synchronous library. The only thing that is asynchronous is writing the PDF to disk. So your code should not need to await anything within the createTicketPDF function. You do need to listen for the stream to finish though, so you could return a promise that resolves when the stream is finished, like this:
const createTicketPDF = (tickets, userEvent) => {
const doc = new pdf
const stream = doc.pipe(fs.createWriteStream(`./tickets/${tickets[0]._id}.pdf`))
tickets.map((ticket, index)=> {
return(
doc.fontSize(30),
doc.text(userEvent.title),
doc.fontSize(10),
doc.moveDown(),
doc.text(`Venue: ${userEvent.venue}, ${userEvent.address1} `),
doc.moveDown(),
doc.fontSize(20),
doc.text(`Ticket ${index+1} of ${tickets.length}: ${ticket.ticketType}`),
doc.image(`./qrCodes/${ticket._id}.png`, {
align: 'center',
valign: 'center'
}),
doc.addPage()
)
})
doc.end()
const promise = new Promise((resolve) => {
stream.on("finish", () => {
resolve();
})
});
return promise;
}
const emailTickets = async (userEvent, tickets) => {
await createQRCodes(tickets)
await createTicketPDF(tickets, userEvent)
await sendGridEmail(emailDetails, true)
}
const accounts = await web3.eth.getAccounts();
App = {
load: async () => {
await App.loadWeb3(
await App.loadAccount()
)
},
loadWeb3: async () => {
if (typeof web3 !== 'undefined') {
App.web3Provider = web3.currentProvider
web3 = new Web3(web3.currentProvider)
} else {
window.alert("Please connect to Metamask.")
}
// Modern dapp browsers...
if (window.ethereum) {
window.web3 = new Web3(ethereum)
try {
// Request account access if needed
await ethereum.enable()
// Acccounts now exposed
web3.eth.sendTransaction({/* ... */})
} catch (error) {
// User denied account access...
}
}
// Legacy dapp browsers...
else if (window.web3) {
App.web3Provider = web3.currentProvider
window.web3 = new Web3(web3.currentProvider)
// Acccounts always exposed
web3.eth.sendTransaction({/* ... */})
}
// Non-dapp browsers...
else {
console.log('Non-Ethereum browser detected. You should consider trying MetaMask!')
}
},
loadAccount: async () => {
App.account = web3.eth.accounts[0]
console.log(App.account)
}
}
$(() => {
$(window).load(() => {
App.load()
})
})
The error is in LINE 1 where I get the accounts from Ganache but await is valid only for async.
What changes should I make in this code to remove the error? Please help me.
If I remove this line the error says that it cannot access accounts and after this await does not work.
Is there any way to make this piece of code in the form of an ASYNC function?
await calls can only be made in functions marked as async. As you have used await in line 1 it is not wrapped in an async function. You can wrap your code in a async function and then call that function. e.g something like:
const main = async () => { // <- the async wrapper function
const accounts = await web3.eth.getAccounts();
// .... rest of your code
$(() => {
$(window).load(() => {
App.load()
})
})
}
main()
Or if you want to be more advanced and not save the function at all
(async ()=>{
const accounts = await web3.eth.getAccounts();
// .... rest of your code
})() // <- call the function right after declaring it
How can I use Knexjs to fetch data synchronously on a resolver in Apollo GraphQL Server? For instance, if I run the following query:
const resolvers = {
Query: {
MySchema: (_, args, { dataSources }, info) => {
var result = db.knex.select('*')
.from('SomeTable')
.where({SomeColumn:'SomeValue'})
.then(function(rows) {console.log(rows)})
.catch(function(error) {console.error(error)});
// Do something with the result here..
console.log(result);
return db.knex.select('*').from('SomeOtherTable')
}
}
}
The line console.log(result); just displays a Promise {<pending>} and by the time the line .then(function(rows) {console.log(rows)}) is executed (asynchronously), the main function will be already finished.
Is there a way to get the result of the database query instead of a Promise on line console.log(result);?
If you want your request to run synchronously and wait the result you need to add async/await
MySchema: async (_, args, { dataSources }, info) => {
var result = await db.knex.select('*')
.from('SomeTable')
.where({SomeColumn:'SomeValue'})
// result is now a value and not a promise
console.log(result)
}
As you noticed, I removed the .then and the .catch that are not relevant anylonger.
I highly recommend adding a try/catch around your awaited promise to avoid uncaught error.
MySchema: async (_, args, { dataSources }, info) => {
try {
var result = await db.knex.select('*')
.from('SomeTable')
.where({SomeColumn:'SomeValue'})
// result is now a value and not a promise
console.log(result)
} catch (e) {
// handle e
}
}
By the way, the return db.knex.select('*').from('SomeOtherTable') would benefit an await aswell to return data instead of a promise.
I have the following JS function:
func() {
return fetch({
...
}).then({
...
})catch({
...
})
}
In it I return a promise returned by fetch(). In the event that it fails (ie calls catch() block) I want to repeat the whole thing. Something like having the whole thing in a while (true) loop, but I can't figure out how to do this with promises.
Any suggestions?
you should have a close look to promises and async await.
async function fetchUntilSucceeded() {
let success = false;
while(!success) {
try {
let result = await fetch(...);
success = true;
//do your stuff with your result here
} catch {
//do your catch stuff here
}
}
}
If you just need the results:
async function fetchUntilSucceeded() {
while(true) {
try {
return await fetch(...);
}
}
}
But be careful with such code as it might never resolve! also it can send a lot of requests without any waittime in between.
You can simply write a loop and count down the attempts until one succeeds or you run out. async/await makes this easy. See below for a minimal, complete example.
Note that the fetch API uses the response.ok flag to ensure that the response status falls in the 200 range. Wrapping with a try/catch is only sufficient to cover connection failures. If the response indicates a bad request, a retry is likely inappropriate. This code resolves the promise in such cases but you could consider !response.ok as an error and retry if you wish.
const fetchWithRetry = async (url, opts, tries=2) => {
const errs = [];
for (let i = 0; i < tries; i++) {
// log for illustration
console.log(`trying GET '${url}' [${i + 1} of ${tries}]`);
try {
return await fetch(url, opts);
}
catch (err) {
errs.push(err);
}
}
throw errs;
};
fetchWithRetry("https://httpstat.us/400")
.then(response => console.log("response is OK? " + response.ok))
.catch(err => console.error(err));
fetchWithRetry("foo")
.catch(err => console.error(err.map(e => e.toString())));
fetchWithRetry("https://httpstat.us/200")
.then(response => response.text())
.then(data => console.log(data))
.catch(err => console.error(err));
Pass the tries parameter as -1 if you want an infinite number of retries (but this doesn't seem like the common case to me).
I have a function like this (the code is simplified to make this code more readable)
import Adapter from "adapter-package";
const adapter = new Adapter();
async powerOn(): Promise<MyClass> {
const myClass = new MyClass();
await adapter.powerOn();
return myClass;
}
as you can see I am using await on the call of adapter.powerOn(). Now I am writing unit tests for this using Jest.
it("can power on the adapter", async () => {
const spy = jest.spyOn(Adapter.prototype, "powerOn");
const myClass = await MyClass.powerOn();
expect(myClass).toBeInstanceOf(MyClass);
expect(spy).toHaveBeenAwaitedFor();
^^^^^^^^^^^^^^^^^^^^
spy.mockRestore();
}, 10000);
The test that I have underlined does not exist, but it is what I would like to test. Can I know if a method I have called has been waited for?
edit:
Will Jenkins pointed out that it is not clear what I am asking. Basically I want to make sure that the Promise returned by adapter.powerOn() has been resolved by the time my function has been resolved. Because I already had an issue where I accidentially had removed the await before adapter.powerOn().
async powerOn(): Promise {
const myClass = new MyClass();
adapter.powerOn();
return myClass;
}
So when I called my function with
await powerOn();
that function was resolved, but it did not await the call to adapter.powerOn() and I had to spend some time debugging. So I would like to make sure that adapter.powerOn() is resolved by the time await powerOn() completes.
How about something like this?
it("can power on the adapter", async () => {
let hasResolved = false;
const spy = jest.spyOn(Adapter.prototype, "powerOn");
const myClass = await MyClass.powerOn();
expect(myClass).toBeInstanceOf(MyClass);
let hasResolved = false;
//if it has been awaited then this should complete
spy.returnValues[0].then(()=>{
hasResolved=true;
})
expect(hasResolved).to.be.true;
spy.mockRestore();
}, 10000)
You could use setInmediate to wait for all pending promises:
const flushPromises = () => new Promise(setImmediate);
it("can power on the adapter", async () => {
const spy = jest.spyOn(Adapter.prototype, "powerOn");
await expect(MyClass.powerOn()).resolves.toBeInstanceOf(MyClass);
await expect(flushPromises()).resolves.toBeTruthy();
}, 10000);
This test first checks if powerOn resolves and returns an instance of MyClass and then checks if every pending promise has been resolved or rejected.
This is the solution I found:
import { inspect } from "util";
it("waits for the adapter to have been powered on", async () => {
const spy = jest
.spyOn(Adapter.prototype, "powerOn")
.mockImplementation(() => new Promise<void>(res => setTimeout(res, 0)));
await Sblendid.powerOn();
const spyPromise = spy.mock.results[0].value;
expect(inspect(spyPromise)).toBe("Promise { undefined }");
}, 10000);
the line
spy.mock.results[0].value
will get me the promise returned by my mock. Using util.inspect on a promise
expect(inspect(spyPromise)).toBe("Promise { undefined }");
will give me "Promise { undefined }" on a fulfilled promise (that is returning undefined) and would return "Promise { <pending> }" if it weren't fulfilled, that is how I test it has been resolved (fulfilled) when await Sblendid.powerOn(); completed.