Why is completion of my beforeAll() function not being waited for? - javascript

I have a web app which I am writing automation for, using WebDriver and Jasmine. This app can be thought of as an online library, where it can load different products, each of which have different content. This automation is supposed to loop over all the locations within the loaded product and run tests on each one.
Since I am running the automation on different products, the automation takes as input an identifier for the product being tested. I use that identifier in the beforeAll function to load information from a web service about the product being tested.
const identifier = process.argv[3];
describe('product check', function() {
var appData;
beforeAll(async function() {
// this comes from a web service, hence being async
appData = await getAppData(identifier);
})
}
The automation should then loop over the appData data structure and generate expectations based on its contents.
Now, my understanding of using loops within Jasmine is that you need to need to put your expectations in a function and call that repeatedly within the loop:
// this won't work
for(var i = 0; i<appData.numInstances; i++) {
it('is within a for loop', async function() {
expect(...);
})
}
// it has to be done like this instead
function containsIt(i) {
it('is within a function', async function() {
expect(...);
})
}
for(var i = 0; i<appData.numInstances; i++) {
containsIt(i)
}
What I am finding is that, if I have my expectations within a function as shown above, the automation doesn't wait for the beforeAll function to finish before the function containing it() is called, so I get an error:
TypeError: Cannot read property 'numInstances' of undefined
I have confirmed that getAppData() works correctly, and that appData is being populated within the beforeAll() function.
I realize that I could put the loop over appData within an it(), but that would mean that all my expect() statements are within the same it() block, and I would lose the ability to have meaningful descriptions from it() in my reporting.
What I would like is the ability to have a loop in which the different functions containing it()s are called repeatedly, while still loading my application data in the beforeAll function. Is this possible? If so, how?

I have faced this exact same situation before and I think with newer versions of Jasmine you won't face this issue although I am not 100% certain.
Looking at the docs they have an async/await in the beforeEach so I am thinking in newer versions it waits for everything to finish in the beforeEach (or beforeAll) before proceeding to its.
To get around your issue, you can use the done callback provided by Jasmine.
beforeAll(async function(done) { // add done callback to the function
// this comes from a web service, hence being async
appData = await getAppData(identifier);
done(); // call done here to let Jasmine know this beforeAll is done
})
With the done callback, the order should now be respected where the beforeAll will run and complete before the first it.

Related

Javascript / Nodejs use await on top level in nodejs module

I tried finding the solution to my problem, but couldnt find it, and was looking for some "best practice examples". I have a nodejs express application and my functions are split in files. For example I have this controller (oktacontroller.js):
var okta_api_key = <here some await data, getting from db>;
const OKTA_ORG_URL = '<here too>';
exports.createUser = async (req, res) => {
console.log(okta_api_key);
}
exports.getGroups = async (req, res) => {
console.log(okta_api_key);
}
In both exported functions (which are express routes) I need the var okta_api_key. I know I can get them by querying them in both functions, because they are async functions and I can use await there, but it feels dumb to query this every time (because it wont ever change).
How can I manage this? I know I can do this:
var okta_api_key;
(async () => {
okta_api_key = await <getting key async>
})()
But this feels off as well..
Is there any way too make some sort of large function, which is async, and exports the two functions? In other words: How do I use await on the top level of a file (module). It doesnt need to be on top level (its impossible), but some sort of method to have my "top level" variables exposed to my exported functions.
EDIT: Some other usecase, because I got the suggestion of putting it in a config file. Yes, for this one it is possible, but for example: I have some other api key which gets his access token from the service itself, on every startup (because it expires). That token cannot be stored in the config file, so I need some async work to get the value. I know top-level-await is not working (or even not desirable), but I just want an example of how you guys would do this if it were your project :)
You are close:
var okta_api_key = (async () => {
return await <getting key async>
})();
Create a promise, then await that promise whenever you want to use it.
How do I use await on the top level of a file (module).
Top level await might look great: You just add one await and then you can access the variable synchronously. But that simplifies things too much: It will stop all modules depending on that module from executing. In most cases you don't want that¹. Instead create a promise of the async task, then await it when needed. That way you limit the asynchronous execution to the code pieces that actually need it.
¹ Those rare cases are:
1) Loading some global config, that you have to access everywhere in your code, so it makes no sense to start the service if the config isn't ready.
2) awaiting in the top level file of your service: As no module depends on it, this won't cause any problems.
Side note: Top level await is not yet specified, and the NodeJS support is also not there yet. To use it in production you have to wait a few months (?).

Difficulty accessing window object in Cypress

I'm trying to access the window object of my App in Cypress in the following manner.
cy.url().should('include', '/home').then(async () => {
const window = await cy.window();
console.log(window);
});
The above method is not working for me as window is returned as undefined.
However, the answer in this SO post states the following:
Or you can use cy.state('window') which returns the window object
synchronously, but this is undocumented and may change in the future.
This method does return the window value successfully.
cy.url().should('include', '/home').then(async () => {
const window = cy.state('window');
console.log(window);
});
As the answer suggests, cy.state('window') is undocumented so I would still rather use cy.window(). Is there any reason why it's returning undefined? (I started learning cypress today.)
This comes up frequently. Cypress has some documentation stating Commands are not Promises. I did a write up using a custom command to force a Command Chain to behave like a promise, but it is still experimental and nuanced.
First I'll give your example almost verbatim to what you're trying to accomplish:
cy.url().should('include', '/home').then(() => {
cy.window().then(win => {
console.log(win) // The window of your app, not `window` which is the Cypress window object
})
})
Your example could be written a number of ways, but maybe explaining a bit how Cypress works will help more.
Cypress has something called "Commands" that return new "Chainers". It is fluid syntax like JQuery:
// fill and submit form
cy
.get('#firstname')
.type('Nicholas')
.get('#lastname')
.type('Boll')
.get('#submit')
.click()
You can (and should) break up chains to be more like sentences:
// fill and submit form
cy.get('#firstname').type('Nicholas')
cy.get('#lastname').type('Boll')
cy.get('#submit').click()
You might have guessed that all Cypress Chainer commands are asynchronous. They have a .then, but they aren't actually promises. Cypress commands actually enqueue. Cypress hooks into mocha's lifecycle to make sure a before, beforeEach, it, afterEach, after block waits until Cypress commands are no longer enqueued before continuing.
Let's look at this example:
it('should enter the first name', () => {
cy.get('#firstname').type('Nicholas')
})
What actually happens is Cypress sees the cy.get Command and enqueues the get command with the argument '#firstname'. This immediately (synchronously) returns execution to the test. Cypress then sees the cy.type command with the argument 'Nicholas' and returns immediately to the test. The test is technically done at this point since there is no done callback and no Promise was returned. But Cypress hooks into the lifecycle of mocha and doesn't release the test until enqueued commands are completed.
Now that we have 2 enqueued commands and the test is waiting for Cypress to release the test, the get command is popped off the queue. Cypress will try to find an element on the page with an id of firstname until it finds it or times out. Assuming it finds the element, it will set a state variable called subject (cy.state('subject'), but don't rely on that). The next enqueued command type will grab the previous subject and attempt to type each key from the string 'Nicholas' one at a time with a default delay of 50ms until the string is done. Now there are no more enqueued commands and Cypress releases the test and the runner continues to the next test.
That was a bit simplified - Cypress does a lot more to make sure .type only runs on elements that can receive focus and are interactable, etc.
Now, knowing this, you can write your example a bit more simply:
cy.url().should('include', '/home')
// No need for `.then` chaining or async/await. This is an enqueued command
cy.window().then(win => {
console.log(win)
})
For me, the accepted answer is good but not really showing me what is necessary.
For me, it is awesome that everything is synchronous and you can do things like
let bg1 = null;
// simply store the value of a global object in the DOM
cy.window().then((win) => {
bg1 = win.myPreciousGlobalObj.color;
});
// Do something that changes a global object
cy.get('a.dropdown-toggle').contains('File').click();
cy.window().then((win) => {
const bg2 = win.myPreciousGlobalObj.color;
// check if nodes and edges are loaded
expect(bg1 != bg2).to.eq(true);
});
Here the interesting thing is even the things inside the then blocks are synchronous. This is so useful.

Jest unit testing - Async tests failing Timeout

I got the following error message "Timeout - Async callback was not invoked within timeout specified by jasmine.DEFAULT_TIMEOUT_INTERVAL." On top of that, I also received an error message stating that 0 assertions were executed while expecting 2 assertions.
I've tried extending the timeout to 10 seconds using jest.setTimeout(10000), which should be more than sufficient time to execute that code, but the problem persisted.
I know m.employeeGetAll() works because when I test my web app using the browser, I can see the list of employees in the view.
Here's how my test looks like
it('Lists all employees successfully', () => {
expect.assertions(2);
return m.employeeGetAll().then(result => { //m.employeeGetAll() returns a promise
expect(result).toBeDefined();
expect(result.length).toBe(3);
});
});
The problem I've discovered, was the way async code works.
What cannot be seen in the code snippet was the call to mongoose.connection.close(); at the very end of my test file.
This call must be done inside afterEach() or afterAll() function for Jest unit testing framework. Otherwise, the connection to the database will be closed before the tests could complete since all of the calls in my controller methods are asynchronous; This leads to no promise ever being returned and the code goes into timeout.
Since I'm using beforeAll() and afterAll(), to load data from database once before the start of all tests and to clear the database at the end of all tests, I've included the call to connect to DB using mongoose inside beforeAll() as well.
Hope this helps someone who's also stuck in my situation.
with async you have to call done
it('Lists all employees successfully', (done) => {
expect.assertions(2);
return m.employeeGetAll().then(result => { //m.employeeGetAll() returns a promise
expect(result).toBeDefined();
expect(result.length).toBe(3);
done();
});
});

Running multiple concurrent scripts in node.js 'vm' module

Regarding the 'vm' module in node.js, what is the best approach to instantiating and running scripts in multiple 'vm's? My objective is to create multiple 'vm's and be able to run the scripts in each one concurrently.
Some specific questions I haven't been able to find any answers on:
Does each 'vm' run on its own thread (or as a separate process)?
Is a callback mechanism supported so the script can let the calling code know it's done, etc.?
Are Promises or async/await supported?
I believe the answers are out there, but since 'vm' is such an overused term, I've had a difficult time finding much useful information.
For your use case it is seems better to use child_process.fork() module from node.js running scripts in vm is not considered a good practice, and is very hard to ensure security.
child_process already has the callback mechanism implemented so you can easily assign handlers on finish.
In order to answer your questions I made a little script:
const vm = require('vm');
const sandbox = {
cb: (err, data) => {
if (err) console.log(err);
console.log(data);
},
};
const code = `
function test() {
cb(null, 'from vm');
}
test();
`;
process.nextTick(() => {
console.log('from event loop');
});
vm.runInNewContext(code, sandbox);
The results of the scripts are:
from vm
from event loop
Which gave me the following conclusions:
Does each 'vm' run on its own thread (or as a separate process) ?
No, vm runs synchronously. if it was running asynchronously the process next tick pushes the console.log() in front of libuv queue so we would get from event loop first and from vm second.
Is a callback mechanism supported so the script can let the calling code know it's done, etc.?
Actually it is not supported natively but you can manage to write code in the manner I did with callback in the sandbox, the single shared state between vm and your code is the global variable.

AngularJS $http.get async execution order

I recently did a lot of coding in AngularJS. After some time it started to feel comfortable with it and also got really productive. But unfortunately there is this one thing I don't understand:
Within my project I need to get data through $http.get and a RESTful API server. This is where I started to stumble first. After implementing promise ($q.defer etc and .then) at functions which are processing data that's necessary to continue, I thought I conquered the problem.
But in this code:
$scope.getObservationsByLocations = function() {
var promise = $q.defer();
var locationCount = 0;
angular.forEach($scope.analysisData, function(loc) { // for each location
$http.get($scope.api + 'Device?_format=json', { // get all devices
params: {
location: loc.location.id
}
}).then(function (resultDevices) {
var data = angular.fromJson(resultDevices);
promise.resolve(data);
// for each device in this location
angular.forEach(angular.fromJson(resultDevices).data.entry.map(function (dev) {
http.get($scope.api + 'Observation?_format=json', { // get all observations
params: {
device: dev.resource.id
}
}).then(function (resultObservations) {
var observations = angular.fromJson(resultObservations);
// for each obervation of that device in this location
angular.forEach(observations.data.entry.map(function(obs) {
$scope.analysisData[locationCount].observations.push({observation: obs.resource});
}));
})
}))
});
locationCount++
});
return promise.promise
};
I can't understand in which order the commands are executed. Since I use the Webstorm IDE and it's debugging feature, it would be more accurate to say I don't know why the commands are executed in an order I don't understand.
Thinking simple, everything included in the forEach have to be executed before the return is reached, because $http.get's are connected through .then's. But following the debugging information, the function iterates over locationCount++ and even returns the promise before it goes deeper (meaning after the first .then() ).
What's that all about? Did I misunderstood this part of the AngularJS concept?
Or is this just really bad practice and I should reach out for a different solution?
If the context is important/interesting: Objects are based on i.e. https://www.hl7.org/fhir/2015May/location.html#5.15.3
With JavaScript you can create only single thread applications, although e.g. here they say it is not guaranteed to be like that.
But we are talking about the real world and real browsers, so your code sample is running as a single thread (by the way the same thread is also used for rendering your CSS and HTML, at least in Firefox).
When it comes to an asynchronous call
$http.get($scope.api + 'Device?_format=json', {
it says "hey, I can do that later". And it waits with that because it must go on with the current thread.
Then, once the current task is done with return it finally can start getting the remote data.
Proof? Check this fiddle:
console.log(1);
for (var i=0;i<1000000;i++) setTimeout(function(){
console.log(2);
},0);
console.log(3);
You see the spike with the for loop? This is the moment when it registers the setTimeout asynchronous calls. Still 3 is printed before 2 because the task is not done until the 3 is printed.
The $http.get is asynchronous, so depending on (among other things) how large the fetched data is, the time it takes to 'complete' the get is variable. Hence why there is no saying in what order they will be completed

Categories