Difficulty accessing window object in Cypress - javascript

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.

Related

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

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.

Chaining functions in Cypress

Context
I am having trouble trying to understand when asynchrony in Cypress should be handled by the developer and when not (because it is handled under the hood).
Consider these 2 tests:
A)
it('stackoverflow example',() => {
cy.get('#soSection').should('contain', 'Stack Overflow').then(() =>{
cy.get('#soButton').click().then(() => {
cy.get('#soSection').should('not.contain', 'Stack Overflow');
});
});
});
B)
it('stackoverflow example',() => {
cy.get('#soSection').should('contain', 'Stack Overflow');
cy.get('#soButton').click();
cy.get('#soSection').should('not.contain', 'Stack Overflow');
});
#soSection will contain 'Stack Overflow' until we click the button. Therefore, both lines should run someway synchronously.
(A) uses then which enables you to work with the subject yielded from the previous command. (B) does not use it.
What I've tried so far
I've run both (A) and (B) tests. Both methods work well and behave the same way.
Doubts
If we needed to use the previous subject then we should only use the method (A) with chainables. Are there any other cases where we MUST use chainables like in (A) example?
Look at the 3 lines of code in (B). We expect those 3 lines run in that order to success. However, Cypress claim to run asynchronously. Therefore, how can we be sure those 3 lines will run in the order expected?
You should read the page called Introduction to Cypress from the Official documentation, or more generally everything under the "Core Concepts" menu. Most of these topics and concepts are explained there.
Command yields
Your assumption is correct, if you need the previous subject you need to use then since Cypress is async and doesn't return the subject. Instead it yields it. This is stated as a core concept of subject management:
Cypress commands do not return their subjects, they yield them. Remember: Cypress commands are asynchronous and get queued for execution at a later time. During execution, subjects are yielded from one command to the next, and a lot of helpful Cypress code runs between each command to ensure everything is in order.
The subject management section shows yielding examples:
Some methods yield null and thus cannot be chained, such as cy.clearCookies().
Some methods, such as cy.get() or cy.contains(), yield a DOM element, allowing further commands to be chained onto them (assuming they expect a DOM subject) like .click() or even cy.contains() again.
Each command yields something different. You can check this in the documentation for each command. For example, cy.children's yield section states that it returns a DOM element.
Technically you doesn't even need to add cy. before each command, since all of them return cy making every command chainable. Usually you only need to use .cy at the start of a command chain (like in the beginning inside a then block). This is more of a code style question.
Command execution
When you run the 3 cy.get lines the commands themselves are not going to be executed, they are just going to be added to a queue by Cypress. Cypress will track your commands and run them in order. Each command has a timeout and will wait for the command condition to be satisfied in that time frame.
As the Commands Are Asynchronous section states:
Cypress commands don’t do anything at the moment they are invoked, but rather enqueue themselves to be run later. This is what we mean when we say Cypress commands are asynchronous.
Technically I should cite the whole page of the documentation because it is really well structured, concise and detailed with a lot of example code. I highly recommend reading it.
Summary
In short, with cy. calls you queue up commands to run for Cypress. If you need to run your own synchronous code, or need the yielded subject you should add a .then() after the Cypress command you would like to run that code. You could write all your consecutive commands in a .then() but it is unnecessary, you just recreate the JS then Christmas tree of doom AKA the callback hell. So only use then if you need the yielded subject of the previous command or want to inject synchronous code. Of course, after some synchronous code (like an if condition) you need to call cy. again and they will be queued up inside that then. Whether you add your remaining cy. commands inside the then or one or more levels above, depends on if you need to work on top of the stuff happening inside the given then block or how you prefer to style your code. Here is an example from the docs modified for demonstration:
it('test', () => {
cy.visit('https://app.com')
// these lines will be queued up and will be run by Cypress in order
cy.get('ul>li').eq(4)
cy.get('.nav').contains('About')
// we want to use the .user field's value
cy.get('.user')
.then(($el) => {
// this line evaluates after the .then() executes
let username = $el.text()
// synchronous code to decide which commands to run (queue up)
if (username) {
// "queue up" other commands
cy.contains(username).click()
cy.get('ul>li').eq(2)
} else {
cy.get('My Profile').click()
}
})
// command that doesn't depend on data inside the `then` block
cy.get('.status').contains('Done')
})

mdg:validated-method _execute asynchronous issues

I'm running into problems with the validated method package in my app tests. I'm calling my methods through the _execute function in order to be able to pass a userId to simulate a logged-in user while testing. The problem is that my asserts right underneath that _execute are called before the method has a chance of completing. I know my test works though because it only happens sometimes, mostly because mongo isn't always returning results quite as fast.
I looked around and found a todos app that uses the _execute function in its tests. I can't get those tests to fail no matter how many times I rerun them, though.
This is an example of my test code.
describe('clients.add', function() {
it('should add an empty (draft) client', function() {
const res = clients_add._execute({ userId: 'CURRENTUSERID' }, { company_id: c1._id });
assert.instanceOf(res, Mongo.ObjectID, 'method returns the newly created clients ID');
const db_client = Clients.findOne(res);
assert.isTrue(db_client.draft, 'client is drafted');
assert.isDefined(db_client.created, 'there\'s a created date');
});
});
clients_add does quite a few permission checks and can therefor take a little while before completing. Rerunning this test 20 times will fail about 5 times and pass the other 15.
Shouldn't the _execute function be synchronous? How do I make it? What am I missing?
In server code, if you provide a callback to database modification functions like insert, it returns the created ID instantaneously, and runs the callback only once the database has acknowledged the write. If you don't provide a callback, the insert call is synchronous and throws an error if the operation fails. See more about this in Meteor docs.
It seems that you have provided an error-handling callback to the insert-function in your method code. This causes the inconsistent behavior, since the database might not actually have had time to do the write before you call findOne in your test. Also, this is redundant since if an error occurs in the insert, the method has already returned and the error is never shown to the user. It's better to simply omit the error-handling callback altogether:
return Clients.insert(new_client);

How do I handle an array of promises using Protractor w/ chai-as-promised

I am using Protractor with CucumberJS and chai-as-promised (given that CucumberJS does not have a built-in assertions library) to build an automated test suite.
Everything works fine for single assertions (using the expect feature of chai-as-promised). I run into trouble, however, when attempting to handle multiple promises within the same test (step). In the following example, verifyUserFirstName returns a promise mapped to a certain row's td.getText().
this.Then(/^I should see my user entry with proper values in the list$/, function (callback) {
expect(usersPage.verifyUserFirstName('tony#gmail.com')).to.eventually.equal('Tony');
expect(usersPage.verifyUserLastName('tony#gmail.com')).to.eventually.equal('Bui');
expect(usersPage.verifyUserPhone('tony#gmail.com')).to.eventually.equal('8764309111');
callback();
Currently, when any of the expect() lines fail, Protractor will exit and leave the browser window hanging without running the rest of the tests.
When a step featuring just a single expect() fails (see example below), everything works perfectly. It is logged as a failed step, and Protractor continues to run the rest of the tests to completion. Has anyone experienced this?
this.Then(/^I should be directed to the user list page$/, function (callback) {
expect(browser.getCurrentUrl()).to.eventually.equal('http://localhost:9001/#/nav/').and.notify(callback);
});
I had same challenge, this is how I solved:
this.Then(/^I should see my user entry with proper values in the list$/, function (callback) {
var verifyUser = Q.all([
usersPage.verifyUserFirstName('tony#gmail.com'),
usersPage.verifyUserLastName('tony#gmail.com'),
usersPage.verifyUserPhone('tony#gmail.com')
]);
expect(verifyUser).to.eventually.deep.equal(['Tony', 'Bui', '8764309111').and.notify(callback);
}
I hope that helps!

Synchronous promise resolution (bluebird vs. jQuery)

I have developed a small lib for the Dynamics CRM REST/ODATA webservice (CrmRestKit). The lib dependes on jQuery and utilizes the promise-pattern, repectivly the promise-like-pattern of jQuery.
Now I like to port this lib to bluebird and remove the jQuery dependency. But I am facing a problem because bluebird does not support the synchronous resolution of promise-objects.
Some context information:
The API of the CrmRestKit excepts an optional parameter that defines if the web-service call should be performed in sync or async mode:
CrmRestKit.Create( 'Account', { Name: "foobar" }, false ).then( function ( data ) {
....
} );
When you pass "true" or omit the last parameter, will the method created the record in sync. mode.
Sometimes it is necessary to perform a operation in sync-mode, for instance you can write JavaScript code for Dynamics CRM that is involed for the save-event of an form and in this event-handler you need to perform sync-operation for validation (e.g. validate that a certain number of child-records exist, in case the right number of records exist, cancel the save-operation and show an error message).
My problem now is the following: bluebird does not support the resolution in sync-mode. For instance when I do the following, the "then" handler is invoked in async fashion:
function print( text ){
console.log( 'print -> %s', text );
return text;
}
///
/// 'Promise.cast' cast the given value to a trusted promise.
///
function getSomeTextSimpleCast( opt_text ){
var text = opt_text || 'Some fancy text-value';
return Promise.cast( text );
}
getSomeTextSimpleCast('first').then(print);
print('second');
The output is the following:
print -> second
print -> first
I would expect that the "second" appears after the "first" because the promise is already resolved with an value. So I would assume that an then-event-handler is immediately invoked when applied on an already resolved promise-object.
When I do the same (use then on an already resolved promise) with jQuery I will have my expected result:
function jQueryResolved( opt_text ){
var text = opt_text || 'jQuery-Test Value',
dfd = new $.Deferred();
dfd.resolve(text);
// return an already resolved promise
return dfd.promise();
}
jQueryResolved('third').then(print);
print('fourth');
This will generate the following output:
print -> third
print -> fourth
Is there a way to make bluebird work in the same fashion?
Update:
The provided code was just to illustrate the problem. The idea of the lib is: Regardless of the execution-mode (sync, async) the caller will always deal with an promise-object.
Regarding "... asking the user... doesn't seems to make any sense": When you provide two methods "CreateAsync" and "CreateSync" it is also up to the user to decide how the operation is executed.
Anyway with the current implementation the default behavior (last parameter is optional) is a async execution. So 99% of the code requires a promise-object, the optional parameter is only use for the 1% cases where you simply need a sync execution. Furthermore I developed to lib for myself and I use in 99,9999% of the case the async mode but I thought it is nice to have the option to go the sync-road as you like.
But I thinks I got the point an sync method should simply return the value. For the next release (3.0) I will implement "CreateSync" and "CreateAsync".
Thanks for your input.
Update-2
My intension for the optional parameter was to ensure a consistend behavior AND prevent logic error. Assume your as a consumer of my methode "GetCurrentUserRoles" that uses lib. So the method will alway return an promise, that means you have to use the "then" method to execute code that depends on the result. So when some writes code like this, I agree it is totally wrong:
var currentUserRoels = null;
GetCurrentUserRoles().then(function(roles){
currentUserRoels = roles;
});
if( currentUserRoels.indexOf('foobar') === -1 ){
// ...
}
I agree that this code will break when the method "GetCurrentUserRoles" changes from sync to async.
But I understand that this I not a good design, because the consumer should now that he deals with an async method.
Short version: I get why you want to do that, but the answer is no.
I think the underlying question being asked is whether a completed promise should immediately run a callback, if the promise has already completed. I can think of a lot of reasons that this might happen - for example, an asynchronous save procedure that only saves data if changes were made. It may be able to detect changes from the client side in a synchronous fashion without having to go through an external resource, but if changes are detected then and only then would an asynchronous operation be required.
In other environments that have asynchronous calls, the pattern seems to be that the developer is responsible for understanding that their work might complete immediately (for example, .NET framework's implementation of the async pattern accomodates this). This is not a design problem of the framework, it's the way it's implemented.
JavaScript's developers (and many of the commenters above) seem to have a different point of view on this, insisting that if something might be asynchronous, it must always be asynchronous. Whether this is "right" or not is immaterial - according to the specification I found at https://promisesaplus.com/, item 2.2.4 states that basically no callbacks can be called until you are out of what I'll refer to as "script code" or "user code"; that is, the specification says clearly that even if the promise is completed you can't invoke the callback immediately. I've checked a few other places and they either say nothing on the topic or agree with the original source. I don't know if https://promisesaplus.com/ could be considered a definitive source of information in this regard, but no other sources that I saw disagreed with it and it seems to be the most complete.
This limitation is somewhat arbitrary and I frankly prefer the .NET perspective on this one. I'll leave it up to others to decide if they consider it "bad code" to do something that might or might not be synchronous in a way that looks asynchronous.
Your actual question is whether or not Bluebird can be configured to do the non-JavaScript behavior. Performance-wise there may be a minor benefit to doing so, and in JavaScript anything's possible if you try hard enough, but as the Promise object becomes more ubiquitous across platforms you will see a shift to using it as a native component instead of custom written polyfills or libraries. As such, whatever the answer is today, reworking a promise in Bluebird is likely to cause you problems in the future, and your code should probably not be written to depend on or provide immediate resolution of a promise.
You might think this is a problem, because there's no way to have
getSomeText('first').then(print);
print('second');
and to have getSomeText "first" printed before "second" when the resolution is synchronous.
But I think you have a logic problem.
If your getSomeText function may be synchronous or asynchronous, depending on the context, then it shouldn't impact the order of execution. You use promises to ensure it's always the same. Having a variable order of execution would likely become a bug in your application.
Use
getSomeText('first') // may be synchronous using cast or asynchronous with ajax
.then(print)
.then(function(){ print('second') });
In both cases (synchronous with cast or asynchronous resolution), you'll have the correct execution order.
Note that having a function being sometimes synchronous and sometimes not isn't a weird or unlikely case (think about cache handling, or pooling). You just have to suppose it's asynchronous, and all will be always fine.
But asking the user of the API to precise with a boolean argument if he wants the operation to be asynchronous doesn't seem to make any sense if you don't leave the realm of JavaScript (i.e. if you don't use some native code).
The point of promises is to make asynchronous code easier, i.e. closer to what you feel when using synchronous code.
You're using synchronous code. Don't make it more complicated.
function print( text ){
console.log( 'print -> %s', text );
return text;
}
function getSomeTextSimpleCast( opt_text ){
var text = opt_text || 'Some fancy text-value';
return text;
}
print(getSomeTextSimpleCast('first'));
print('second');
And that should be the end of it.
If you want to keep the same asynchronous interface even though your code is synchronous, then you have to do it all the way.
getSomeTextSimpleCast('first')
.then(print)
.then(function() { print('second'); });
then gets your code out of the normal execution flow, because it's supposed to be asynchronous. Bluebird does it the right way there. A simple explanation of what it does:
function then(fn) {
setTimeout(fn, 0);
}
Note that bluebird doesn't really do that, it's just to give you a simple example.
Try it!
then(function() {
console.log('first');
});
console.log('second');
This will output the following:
second
first
There are some good answers here already, but to sum up the crux of the matter very succinctly:
Having a promise (or other async API) that is sometimes asynchronous and sometimes synchronous is a bad thing.
You may think it's fine because the initial call to your API takes a boolean to switch off between sync/async. But what if that's buried in some wrapper code and the person using that code doesn't know about these shenanigans? They've just wound up with some unpreditable behavior through no fault of their own.
The bottom line: Don't try to do this. If you want synchronous behavior, don't return a promise.
With that, I'll leave you with this quotation from You Don't Know JS:
Another trust issue is being called "too early." In application-specific terms, this may actually involve being called before some critical task is complete. But more generally, the problem is evident in utilities that can either invoke the callback you provide now (synchronously), or later (asynchronously).
This nondeterminism around the sync-or-async behavior is almost always going to lead to very difficult to track down bugs. In some circles, the fictional insanity-inducing monster named Zalgo is used to describe the sync/async nightmares. "Don't release Zalgo!" is a common cry, and it leads to very sound advice: always invoke callbacks asynchronously, even if that's "right away" on the next turn of the event loop, so that all callbacks are predictably async.
Note: For more information on Zalgo, see Oren Golan's "Don't Release Zalgo!" (https://github.com/oren/oren.github.io/blob/master/posts/zalgo.md) and Isaac Z. Schlueter's "Designing APIs for Asynchrony" (http://blog.izs.me/post/59142742143/designing-apis-for-asynchrony).
Consider:
function result(data) {
console.log( a );
}
var a = 0;
ajax( "..pre-cached-url..", result );
a++;`
Will this code print 0 (sync callback invocation) or 1 (async callback invocation)? Depends... on the conditions.
You can see just how quickly the unpredictability of Zalgo can threaten any JS program. So the silly-sounding "never release Zalgo" is actually incredibly common and solid advice. Always be asyncing.
What about this case, also CrmFetchKit related which in latest version uses Bluebird. I have upgraded from version 1.9 that was based on jQuery. Still the old app code that uses CrmFetchKit has methods the prototypes of which I can't or won't change.
Existing App Code
CrmFetchKit.FetchWithPaginationSortingFiltering(query.join('')).then(
function (results, totalRecordCount) {
queryResult = results;
opportunities.TotalRecords = totalRecordCount;
done();
},
function err(e) {
done.fail(e);
}
);
Old CrmFetchKit implementation (a custom version of fetch())
function fetchWithPaginationSortingFiltering(fetchxml) {
var performanceIndicator_StartTime = new Date();
var dfd = $.Deferred();
fetchMore(fetchxml, true)
.then(function (result) {
LogTimeIfNeeded(performanceIndicator_StartTime, fetchxml);
dfd.resolve(result.entities, result.totalRecordCount);
})
.fail(dfd.reject);
return dfd.promise();
}
New CrmFetchKit implementation
function fetch(fetchxml) {
return fetchMore(fetchxml).then(function (result) {
return result.entities;
});
}
My problem is that the old version had the dfd.resolve(...) where I was able to pass any number of params that I need.
The new implementation just returns, the parent seems to call the callback, I can't call it directly.
I went and made a custom version of the fetch() in the new implementation
function fetchWithPaginationSortingFiltering(fetchxml) {
var thePromise = fetchMore(fetchxml).then(function (result) {
thePromise._fulfillmentHandler0(result.entities, result.totalRecordCount);
return thePromise.cancel();
//thePromise.throw();
});
return thePromise;
}
But the problem is that the callback gets called two times, once when I do it explicitly and second by the framework but it passes it one parameter only. To trick it and "tell" not to call anything because I do it explicitly I try to call .cancel() but it is ignored. I understood why but still how do you do the "dfd.resolve(result.entities, result.totalRecordCount);" in the new version with out having to changes prototypes in the app that uses this library ?
You can in fact do this, yes.
Modify the bluebird.js file (for npm: node_modules/bluebird/js/release/bluebird.js), with the following change:
[...]
target._attachExtraTrace(value);
handler = didReject;
}
- async.invoke(settler, target, {
+ settler.call(target, {
handler: domain === null ? handler
: (typeof handler === "function" &&
[...]
For more info, see here: https://github.com/stacktracejs/stacktrace.js/issues/188

Categories