I am using mocha and selenium-webdriver for E2E tests. Most of the tests are async and I am using async/await functions to handle this. Unfortunately right now I can't get a single one done. Here is what my code looks like:
describe('Some test', function () {
before(function () {
driver.navigate().to('http://localhost:3000')
})
after(function () {
driver.quit()
})
it('should display element', async function () {
let elementFound = false
try {
await driver.wait(until.elementIsVisible(driver.findElement(By.className('element'))), 1000)
assessForm = await driver.findElement(By.className('element')).isDisplayed()
assert.ok(elementFound)
console.log('elementFound', elementFound)
} catch (err) {
console.log(err)
assert.fail(err)
}
})
})
The problem that is happening seems to be that the after function is being called before the test can finish. Here are the error logs:
Error: Timeout of 2000ms exceeded. For async tests and hooks, ensure
"done()" is called; if returning a Promise, ensure it resolves.
{ NoSuchSessionError: no such session (Driver info:
chromedriver=2.36.540469
(1881fd7f8641508feb5166b7cae561d87723cfa8),platform=Mac OS X 10.13.3
x86_64)
at Object.checkLegacyResponse (/Users/me./myproject/node_modules/selenium-webdriver/lib/error.js:585:15)
at parseHttpResponse (/Users/me./myproject/node_modules/selenium-webdriver/lib/http.js:533:13)
at Executor.execute (/Users/me./myproject/node_modules/selenium-webdriver/lib/http.js:468:26)
at
at process._tickCallback (internal/process/next_tick.js:188:7) name: 'NoSuchSessionError', remoteStacktrace: '' }
If I remove my after() function, I still get
Error: Timeout of 2000ms exceeded. For async tests and hooks, ensure
"done()" is called; if returning a Promise, ensure it resolves.
but, my console.log shows that my element has been found.
If I then try making after() async, like this:
after(async function () {
await driver.quit()
})
I get the same error as the first one.
It is also important to note that I have read that I don't have to use done() when I am doing async/await. So what in the world is that all about? And even if I did, I keep getting the same error.
How do I solve this? It seems like everything is in order, but I can't seem to appropriately have the tests run through without running into each other.
Instead of using:
await driver.wait(until.elementIsVisible(driver.findElement(By.className('element'))), 1000)
try:
await driver.wait(until.elementLocated(By.className('element'))).isDisplayed()
Related
My published Cloudflare Worker (wrangler publish --env environment_name) is timing out for clients, but not when
running locally (e.g. by using cfworker, a nice tool to emulate cloudflare workers locally)
or running in preview (wrangler preview --env environment_name).
A summary of my worker:
addEventListener('fetch', async (event) => {
const fetchEvent = event as FetchEvent
const results = await doSomeWork() // returns a promise
return fetchEvent.respondWith(new Response(JSON.stringify(results)))
})
My wrangler tail (production logs) output does complete (after I placed console.log statements in doSomeWork. There were no errors, and I got {"outcome":"ok"... in wrangler tail. I would have expected to get error code 1102 (Worker exceeded CPU time limit.) if time-out was happening.
It turns out that addEventListener cannot be passed an async function (or one that returns a promise). The fetchEvent.respondWith does accept a promise however. This is not written in the documentation, but I discovered this in lib.webworker.d.ts.
To do asynchronous work, you must return a promise to fetchEvent.respondWith instead:
So your alternatives are to:
Pass a promise to respondWith
addEventListener('fetch' (event) => {
const fetchEvent = event as FetchEvent
const responsePromise = doSomeWork().then((results) => new Response(JSON.stringify(results))
return fetchEvent.respondWith(responsePromise))
})
Or pass the result of an async function to respondWith (still a promise, I told you, you must return a promise)
addEventListener('fetch' (event) => {
const fetchEvent = event as FetchEvent
const responsePromise =
return fetchEvent.respondWith(async () => {
// I would put this async function in a different file (handlers.ts/.js), and name it `doSomeWorkHandler` to make it more readable though
const results = await doSomeWork()
return new Response(JSON.stringify({hello: "world"}))
}))
})
Why no timeout error?
The reason the timeout error doesn't happen is because even though Cloudflare Workers limits your CPU execution time to 10ms on the free plan, it doesn't stop your worker because you're not using the CPU in this bug/ edge case. It's doing nothing.
Iām using meteor with check() and audit-check-arguments package.
When I use a meteor method using async/await and pass a parameter, even though I use check() to validate the function parametrs, the audit package still throws an exception indicating that not all input parameters have been checked. If I remove the async/await implementation, the package does not crib. What am I missing?
Example:
Meteor.methods({
test: async function(param1){
check(param1, String);
...
await ....
}
});
Throws an exception:
=> Client modified -- refreshing
I20200513-10:43:27.978(5.5)? Exception while invoking method 'test' Error: Did not check() all arguments during call to 'test'
I20200513-10:43:27.979(5.5)? at ArgumentChecker.throwUnlessAllArgumentsHaveBeenChecked (packages/check/match.js:515:13)
Whereas this traditional meteor method does not throw any exceptions
Meteor.methods({
test: function(param1){
check(param1, String);
...
}
});
I know for sure that I am passing exactly one parameter.
It looks like audit-argument-checks only works for synchronous functions.
I don't have this issue because we use mdg:validated-method, which uses requires you to specify an argument validator for each method.
It shuts up the argument checker by wrapping the method function with this:
// Silence audit-argument-checks since arguments are always checked when using this package
check(args, Match.Any);
The simplest solution I can think of, is to separate the check from the async function. You could use a wrapper function to do this:
function checkAndRun(check, run) {
return function(...args) {
check.apply(this, args);
return run.apply(this, args);
}
}
Meteor.methods({
'example': checkAndRun(
function(exampleID){
check(exampleID, String);
},
async function(exampleID) {
const result = await doSomethingAsync(exampleID);
SomeDB.update({ _id: exampleID }, { $set: { someKey: result.value } });
return result.status;
}
}
});
or you could even do it inline with an async IIFE:
Meteor.methods({
example(exampleID) {
check(exampleID, String);
return (async () => {
const result = await doSomethingAsync(exampleID);
SomeDB.update({ _id: exampleID }, { $set: { someKey: result.value } });
return result.status;
})()
}
});
Which, come to think of it, is much simpler than the simplest solution I could think of at first š
You just want to separate the sync check from the async method body somehow
In case you're curious, let diving through the source to see where it's called. When the method is called (in ddp-server/livedata-server), we end up here, a sync method call for the first reference of audit-argument-checks:
https://github.com/meteor/meteor/blob/master/packages/ddp-server/livedata_server.js#L1767-L1770
Which takes us into check/Match for another sync call here: https://github.com/meteor/meteor/blob/71f67d9dbac34aba34ceef733b2b51c2bd44b665/packages/check/match.js#L114-L123
Which uses the strange Meteor.EnvironmentVariable construct, which under the hood has another sync call: https://github.com/meteor/meteor/blob/master/packages/meteor/dynamics_nodejs.js#L57
In a React Native project, I wrote this function using Promise to do a job asynchronously;
function doEncryptionAsync(params) {
return new Promise(
function (resolve, reject) {
// Async code started
console.log('Promise started (Async code started)');
// The job that takes some times to process
var encrypted_value = new EncryptedValue(params);
if (true) {
resolveencrypted_value
}
else {
reject("Error while encrypting!");
}
}
)
}
And I call that in my Redux action;
export const encrypt = ( params ) => {
return (dispatch) => {
dispatch({
type: type.ENCRYPT
});
// Sync code started
console.log('Started (Sync code started)');
doEncryptionAsync(params)
.then((response) => {
// Async code terminated
console.log('Promise fulfilled (Async code terminated)');
encryptSuccess(dispatch, response);
})
.catch((error) => {
console.log(error);
encryptFail(dispatch);
});
// Sync code terminated
console.log('Promise made (Sync code terminated)');
}
}
It works, but not asynchronously! My main thread seems to be blocked until doEncryptionAsync() returns. The line console.log('Promise made (Sync code terminated)') runs, but not immediately!
My output for logs is like this;
// OUTPUT Simulation
Started (Sync code started) at time x
Promise started (Async code started) at time x
Promise made (Sync code terminated) at time (x + 2sec)
Promise fulfilled (Async code terminated) at time (x + 2sec)
My question is what's wrong with my approach to implement a AsyncTask?!
JavaScript's asynchronous behavior is only relevant for IO blocking functions. Meaning, that instead of waiting for an IO function, the event loop keeps running.
Seeing as JS is single threaded, CPU bounded computations take up the thread, and cannot be done asynchronously.
Your only recourse, then, is to create a native module that will do the calculation in a different thread for you, and then call a JS callback when it's done.
This is one of those problem that you can explain but do not know how to fix it. I have a simple store method:
exports.store = async (req, res) => {
const mydata = new MyModel(req.body)
await mydata.save(function(err,user) {
res.location('/mydata/id/' + user._id)
})
res.status(201).json({ data: userdata })
}
When it runs, I get the following error:
events.js:182
throw er; // Unhandled 'error' event
^
Error: Can't set headers after they are sent.
at validateHeader (_http_outgoing.js:489:11)
at ServerResponse.setHeader (_http_outgoing.js:496:3)
at ServerResponse.header (.../node_modules/express/lib/response.js:730:10)
at ServerResponse.location (.../node_modules/express/lib/response.js:847:15)
at .../src/controllers/users-controller.js:26:13
at .../node_modules/mongoose/lib/model.js:3919:16
at .../node_modules/mongoose/lib/services/model/applyHooks.js:162:20
at _combinedTickCallback (internal/process/next_tick.js:131:7)
at process._tickCallback (internal/process/next_tick.js:180:9)
Process finished with exit code 1
I appears that the callback function runs separately and asynchronously because the res.status(201).json({ data: userdata }) seems to be producing the error and does not let me set the location header.
I've looked around for how to set the location header but none of the answers are like what I'm trying to do. This seems like something that should have been done before...? I'm looking for some help on how to do this...
You are mixing up two way of thinking :
Promises (with async/await in your case)
Callback
Use only one
try {
const user = await mydata.save();
res.location(`/mydata/id/${user._id}`);
// Other stuff ...
} catch(err) {
// Handle the errors
}
here you get an article about Promises into mongoose.
This has been bothering me for a while so I thought I'd just do a quick QA on it:
If one has a normal nodeJS module or something and it has a async function on the server side. How do I make it synchronous. E.g how would I convert the nodejs fs.stat asynchronous function to a synchronous one.
e.g I have
server side js
Meteor.methods({
getStat:function() {
fs.stat('/tmp/hello', function (err, result) {
if (err) throw err;
console.log(result)
});
}
});
If I call it from the client I get back undefined as my result because the result is in a callback.
There is a function (undocumented) called Meteor.wrapAsync.
Simply wrap the function up
Meteor.methods({
getStat:function() {
var getStat = Meteor._wrapAsync(fs.stat);
return getStat('/tmp/hello');
}
});
Now you will get the result of this in the result of your Meteor.call. You can convert any async function that has a callback where the first parameter is an error and the second the result.