That's what happening with Cypress command chains. If I take a look into the EQ command, you can see that it is applied to exactly those two elements that the get command has found. It's the array of two elements. EQ command is using that. And again, it's yielding something, so it has filtered out the second element. It is yielded the second one. We can see this, there's a div, et cetera. So when I click on contains, again, you can see it being applied to that element. The one that the EQ command has passed on. So this is how Cypress works. It's going to be passing information from one command to another until we do something with that, we make an assertion or click on it or do something like that. So yeah, chaining, very important concept in Cypress. All right.
Another very important concept in Cypress is retriability. So let's take a look into the second test. So I save my test, and I can see my test is actually trying to assert that there are five card elements. So I got this card text. It's going to find five elements and it has found them. So the test has passed. Now if I were to change this number from five to six and save my test, you can see that it's actually not failing immediately. It's actually retrying and trying to find those six elements on the page until eventually the test is going to fail. Now, if you are working with Selenium, you might know this as a fluid weighting. I forgot if it was fluid or fluent. One of those. Basically we have a top limit of how long we are, we want to wait until we declared the test is failing. So if I want to have six elements, I can, basically the sixth element can appear during that period of time and the test is going to pass as you can see here, so if I have six, I create another card, now it's passing. Now, there might be another. Oh, one thing I want to point out is that we have this should command that has the assertion that we should have six elements. Now, not only the assertion is retried, but also the previous command is retried. Because if the assertion is not passing, if there are not six elements, we will be requerying the elements on the DOM and basically calling that get command again and again. So, if we have an assertion like this, we can pass a longer timeout and make sure that we wait for a longer period of time. So, let's do timeout and let's do 60 seconds, right? So, when I save that now, and you see that there are not six elements on the page, you can see that Cypress is retrying, retrying, retrained, until eventually when I add that sixth element, the test is going to pass. Now, what can happen is not only this, but we can have an opposite problem. So, what I'm going to do, I have these evil code prepared here and I have this cards load slowly function, which is going to load the cards on my board for a longer time, so this is a hack I have for my application. Now, if you take a look, Oh, let's actually make this insertion to five. So, the test should be passing, right? If you take a look at what's happening here, cards are loading, the test fails, but eventually, our cards appear, right? So, if we have a slow application, this could be a problem, right? Because the cards eventually appear, so we shouldn't declare the test failed, it should probably pass. So, I already shown you the solution. We can make that timeout a little longer, we can make that retrying a little longer, so by default, it's four seconds, but we can make it longer. Let's make it six seconds, so that's time in milliseconds, right? So, when I save it now, and the cards take five seconds to load, they're still going to load in time for the test to pass. And, of course, even if I put like 60 seconds, my test is going to pass right when it finds those five elements, so it's not going to wait a whole 60 seconds, just the maximum amount of time that's needed, and then it proceeds to finish the test or move on to the next command. So, we can change that timeout either on the command level, or we can change that timeout on the test level. So the way we can do that is, as I mentioned, the ID function has two parameters, right? The first one is the name of the test, the second one is callback, but we can actually have the second one be an object and that would be like a test configuration object. So, what we can do in here is to define the default command timeout and say that it should be six seconds. So when I save this, all of the commands will actually now have not the four seconds by default, but six seconds timeout by default. We can also define that not only on our test level, but we can do that for the whole test suite. So, if we go default command timeout, Oh, sorry, that's not an e2e object, that's actually outside of it. We can say that, alright, we're testing a fairly slow application, so let's make the default command timeout, not four seconds, but six seconds instead. So, yeah, that's something you should do. Although I would not recommend putting that command timeout on a like very high number, because not only this means that your tests are going to have a longer refriability, it also means they are going to take much longer time to fail, which might be a problem if you have like hundreds of tests. If you add just one second and 60 of your tests should fail, then you just added one minute of waiting to your test, or not one minute, because if they're failing, they're obviously taking a longer time. So yeah, basically try to keep this number as low as possible, of course, within reasonable constraints. All right, let's put this altogether. So we got chaining, we got retryability. Let's now take a look into the third test where we put those two concepts together. So I have another evil code here and that's load cards randomly. So what this is going to do, let's do three seconds in here, what this is going to do is that it's not all the cards are going to load at the same time but they're going to load randomly so either the cards in the first list get loaded first or cards in the second list will get loaded first. What that means by looking at the test, maybe you are able to tell, maybe you're not, what that means is that we are selecting the cards, right? As soon as we find cards, we want to select the second one and we want to make sure that the text is bread. Now the second one is bread, right? But if we run the test a couple of times, we might get into a situation where our test would fail. Let me try to get to that situation, and here it is. Our test is failing. And why is that? Well, the timeline will actually tell us, right? If I hover over my EQ command, you can see that it's not selecting this first card, right? This one, but it is selecting the second one. Now, why is that? What's the reason behind that? Well, the reason is that the cards in the second list got loaded first, which means that we found some cards. Then we filtered out the second one, which was this soap card in this case, and we asserted that it contains the text breadth. So the problem here is, that when we were talking about retriability, I already kind of mentioned that. When we have an assertion, it is going to make the previous command retry, right? But it's not going to make the whole chain, a retry. So what happens, we will be sort of stuck in an endless loop between these two elements, where between these two commands, where the eq command is just filtering and filtering, and it's still filtering those two elements that the get command has found, because this one is not going to get retried again. And our shoot command is trying to assert something that's just never going to change, because the eq command kind of already just works with those two elements that were there before. So if you take a look into the console, that may be more clear. So the get command will find two elements. As soon as it finds two elements, it's going to move on to the next command.
Comments