The web has evolved. Finally, testing has also. Cypress is a modern testing tool that answers the testing needs of modern web applications. It has been gaining a lot of traction in the last couple of years, gaining worldwide popularity. If you have been waiting to learn Cypress, wait no more! Filip Hric will guide you through the first steps on how to start using Cypress and set up a project on your own. The good news is, learning Cypress is incredibly easy. You'll write your first test in no time, and then you'll discover how to write a full end-to-end test for a modern web application. You'll learn the core concepts like retry-ability. Discover how to work and interact with your application and learn how to combine API and UI tests. Throughout this whole workshop, we will write code and do practical exercises. You will leave with a hands-on experience that you can translate to your own project.
How to Start With Cypress
AI Generated Video Summary
The workshop is led by Filip Nits, a QA lead in Slido, who teaches testers about web development and developers about testing. The workshop focuses on using Cypress for end-to-end testing of the Trello app, covering topics such as installation, writing tests, automating Trello board and element selection, using plugins, and testing cart creation and deletion. It also explores concepts like chaining commands, writing assertions with Chai, sending HTTP requests, intercepting requests, and combining UI and API tests. The workshop provides practical challenges and encourages participants to code along.
1. Introduction to the Workshop
My name is Filip and I have been a QA lead or a QA test Automation lead in Slido for almost 7 years now. Today I like to teach testers about web development and teach developers about testing. You can find everything that I do on my webpage, filibris.com, where I put Cypress tips almost every week. I do workshops, presentations, webinars, and all that. This is a link to my page. So Filip Nits, that's my name,.com slash workshop, where you can find like the full version of what we are going to be experiencing today. If you want to experience the full thing, I have an upcoming workshop actually starting next week. If you are considering doing that, hit me on Discord with a DM. I will give you a promo code.
So, let's dive in. Short introduction. My name is Filip. I have been a QA lead or a QA test Automation lead in Slido for almost 7 years now. So, it's actually, this slide needs an updating. And what I do there is mostly, I do test automation. I do have a background in Psychology, which I guess sort of goes with the teaching, maybe? I don't know. But yeah, that's kind of my background.
Today I like to teach testers about web development and teach developers about testing. You can find everything that I do on my webpage, filibris.com, where I put Cypress tips almost every week. I had quite a big gap because I was doing lots of new courses. And I will be continuing with that. But yeah, the whole page is basically dedicated to Cypress and giving different tips. So if you want to, if you're searching for something, you can search also there. And yeah, so I do workshops, presentations, webinars, and all that. I think I said it all about myself. This is a link to my page. So Filip Nits, that's my name,.com slash workshop, where you can find like the full version of what we are going to be experiencing today. So this is, as I said, not everyone was here. This is sort of a smaller demo version of the workshop, if you will. And if you want to experience the full thing, I have an upcoming workshop actually starting next week. So if you want to sign up for that and get the full experience, you can. And if you are considering doing that, hit me on Discord with a DM. I will have, I'll give you a promo code, because it doesn't really make sense if you do some parts twice. So to get some value of that, I'm going to send you a promo code if you're interested in having the full experience.
2. Workshop Format and Overview
The workshop will consist of a chapter overview, a demo, and practical challenges. During the demo, it is recommended to watch rather than code along to avoid missing important points. The demo will be available in the demo start and demo end files, with additional information in the notes folder. The practical challenges will provide instructions in the challenge.cy.js file and solutions in the challenge solution file. It is not necessary to complete all the challenges, as the goal is to keep participants engaged even after the workshop.
So the workshop form will go like this. You can, by the way, scan this QR code, because we will be using that. But the workshop form goes like this. I'm going to do a chapter overview, basically tell you what you will be learning in that chapter. And then we will go through a demo. Or rather, I will be going through the demo, basically show you something, how everything works. And then we have a practical challenge. So I will be silent and you will be doing the coding work. So feel free to do that.
And for the Q&A, we can absolutely do that continually. In fact, we are a fairly small group. We just have like 15 participants. That includes me and Alex who is on the Zoom call as well, from TestJS crew. So I think we can safely go with just unmuting Mike and asking a question. Feel free to do that. To get some additional value from this. But we can also use Slido. That's where I'm from. It's a great tool for having an interaction. If you are shy or if you feel like you have a stupid question, you don't. But if you feel that way, you can use Slido to ask anonymously. And I'll probably say that's not a stupid question, that's a great question. And I'll answer that. But I also have a question for you. And that's why I wanted you to scan that QR code. So let me jump into that. And my question is how well do you know Cypress? I would like to know where you are at currently with your knowledge. And so one star means that you are just starting. You don't know much or you don't know anything. And six stars would be that you use it daily, you already know your way around, and basically you're here for improving your knowledge. Maybe find some extra tips. Alright, we are very well spread here. Oh we got 1 sixth, that's great. So we are spread on both sides, mostly in the middle. Oh, that's good. That's good. By the way, don't worry if you are too much on the right side or too much on the left side. Normally when I do this I do breakout rooms to break out this meeting into smaller ones. Not sure if we'll be able to do that today, but we'll see. We'll see how we handle that. Maybe we'll stay in the same room. All right. All right. So we got 3.6 the score. We got some beginners, some people that are in the middle, some are more advanced. That's awesome. Thank you. Thank you for answering that question. That really helps to sort of get me to get an idea.
Let me continue with explaining how this workshop is going to work. I already kind of said a little bit about this, but for the demo, I will be doing a short presentation. So during that, don't try to code along. In fact, it's better if you don't because I tend to go a little bit fast. And if you are switching between coding and watching, you might miss some points. So it's better for you to just watch. In order for you to not get lost, you can see everything that I did in the demo end file and I will be starting at the demo start. So that's demo start, demo end. In the project folder, you'll have the name of the chapter. Oops, that was not underlining, but crossing. So you have the name of the chapter we will be in and then I will be always starting from demo start and finished state will be at demo end. So you will get an insight into everything. Plus, everything I plan on seeing is inside the notes folder, but I tend to improvise so you will not find everything, but hopefully you'll find most of the information that you need.
After we do the demo, I'm going to go to the Practical challenges and again, I'm showing you here the folder structure. So in there what you will be doing during the practical challenges is take a look into the challenge.cy.js. Inside there you will find instructions on what you should do and inside the challenge solution you will find the solution for that particular example. Don't feel bad if you look into that. In fact I would encourage you to take a look into that, maximize your learning by getting as much information as you can, don't try to figure it out from memory or something. Use the documentation, use the solution file, do everything you can to gather knowledge. Important note on the practical challenges, you don't need to finish them all. In fact my goal is to keep you active even after the workshop so if you don't finish them all and you're feeling uncomfortable with them not being finished all, that's actually a good thing, I like that, because that will probably force you to open that repository again and play with Cypress a little more.
3. Tools and Application Overview
We will be using VSCode or any other preferred tool with a terminal. Cypress version 10.10 will be used for end-to-end testing. The workshop will focus on testing the Trello app, which allows users to create boards, lists, and cards. The app also features functionality such as checking off completed tasks, drag and drop, bookmarking, and optional login/signup. The backend uses a simple database structure stored in a JSON file. API tools are available to reset the application and clear the database. The workshop will provide a more in-depth understanding of the application.
So yeah, just a note on the amount of practical challenges. Plus it helps leveling up the people that are beginners with people that are already advanced, so not everyone will finish at the same time or do the same amount of practical challenges.
Alright, so let's jump into the tools we will be using. I will be using VSCode, it's okay to use any other tool. If you are using WebStorm, that's totally fine, or Sublime, or I don't know what. Just make sure that you have a terminal, because we will be using that.
As for Cypress, we will be using version 10.10. If you updated Cypress' version, that's no problem, it's totally fine, everything above version 10 is fine. And we will be just doing end-to-end testing, no component testing today. Workshop will be coming in the future.
And we will be using this awesome Trello app. So this is a testing workshop, so we need something to test. And I'm going to show you what we will be testing. So here I have the repository, and I have my Cypress project and everything. We'll go through that. And inside the Trello app, we have the application that we will be testing. Now to run the application, you will go npm start, and this will spin up a local server, so everyone will have their own application at their computer. And if you go to HTTP localhost 3000, you'll find the application opened in here.
So it looks something like this. I made it... Oops, what did I do? And that's not what I wanted. All right. So it looks something like this when you open it for the first time. When you create a new board, it's going to create a new board. So if you're familiar with the Trello app, it's basically a to-do app where you can create multiple to-do lists, like List 1, List 2. And you can create those to-do items, and they're basically called cards here. So Card 1, Card 2. You can take a look into the detail of the card. You can check it off as completed. You can even do that over here. You can also drag and drop those cards. You can drag and drop those lists. Do a bookmark of the board. So you can click on the little star over here. And there's even a login. I'm not sure if we're going to be using that today. We'll see. But yeah, you can do a login or a signup or whatever. And yeah. So that's basically like the functionality.
As far as the database goes, it's actually pretty simple. I really, really like this, this very simple backend we have here. So inside the Trello App folder, we have this backend folder, we have our APIs, blah, blah. Plus we have the data folder. And inside there, there's a database JSON file. And this is basically our database. This is everything it takes. So we got our boards, our cards, our lists. This is basically the whole structure of what's going on here. So when we created that new port, it created this object, this item in our database. It has some additional attributes like start, when was it created, the ID, et cetera. And same goes for all of the other things inside our application. So we got our cards. So, as you can see, here's the card one, cards two that I have just created, and yeah, that's it. So if you want to examine the database and what's going on there, this is the file. And this is where you go to look into.
Also, to make your life a little bit easier, I've created this special, I call them API tools. When you hit your F2 key, if you're on a Mac, you can go Function and F2, you will see these little tools at the bottom right corner. Which can help you to reset your application. So basically, you can go ahead and delete all of the cards from the database, all of the lists, even all of the boards or everything, all of the users and everything that's in sight. And when you do that, and you go back to the database, you'll see it's empty. So this is just a little tool to make your life a little bit easier. And yeah, that's about it. You will get familiar with the application a little bit more, but this is about it for the starters. I think that will be enough. And let's move on.
Oh yeah, one thing that I do in this workshop is you might see a message like this. Database was wiped and seeded before all tests. This is, again, something where I'm trying to make your life a little bit easier, and this is a screenshot from Cypress.
4. Installing and Running Cypress
In this part, we learn how to install Cypress, initialize a project, and run Cypress. We also explore the Cypress GUI and choose a browser for testing. The process involves creating configuration files and scaffolding example spec files. Cypress provides a service called Cypress dashboard for reporting and analytics. The entire process is demonstrated in a demo.
This is, again, something where I'm trying to make your life a little bit easier, and this is a screenshot from Cypress. So if you see this message in Cypress, you will know that the application was basically reset and whatever you created in your application will get wiped out and seeded with some data. With some data that... Yeah, with some data. So just be mindful of this so it doesn't confuse you. Hey, where's my data? I just created a new board, and it's not here anymore. It's something else. So this is the reason why there's a script going on in the background doing this, not for all of the tests, but for some of them.
All right, so let's head on to sort of a chapter zero. Where should I? All right, so I need a little bit of my notes. All right, so in the chapter zero, we will learn how to install Cypress, how does a Cypress project look like, what is the purpose of different project folders, and also how to choose a different browser in Cypress, and yeah, I think that's about it.
So let's jump into the demo. So I have my repository here that we will be using for the workshop, but just for a moment I'm going to open a new window and I'm going to hide this one. And instead of working in my repository, I'm just going to start from scratch. So we are going to be learning now how to start with a Cypress project. So I'm going to create a new folder, let's call it test.js-workshop, and I'll just go ahead and drag and drop it in my VS Code.
So in the VS Code, I have the folder opened, and if I want to start a new Cypress project, the first thing I usually do, although this is not totally required, but it's a good practice is to go npm init and yes. So what this is going to do, it's going to create my package.json file, and since we are in the node environment, this package.json usually contains information about the project we are working in. The script that I just called the npm init-y is basically saying that when I do npm init it's going to sort of give me this little survey or questionnaire, what's the name of your project, what is the version of the project? Basically it's going to ask a couple of questions, dash y means answer yes to all of those questions. If I do that, it's going to create a package.json file like this one, so that's basically a step zero. It is not required if you want to start a new project, but you will need to have a package.json file at some point, so better off to start with that. Alright, so that's step zero, and now how to install Cypress? Since we are in Node environment and we can use npm, we'll do exactly that npm install Cypress, and I'm putting this at latest dag, which I don't really have to do because it's going to install the latest version anyway, but I've been using it for some reason, so it auto-completed for me. Alright, so now I have Cypress installed, and you can see that we have two new items in our project. We got our NodeModules folder and we got our package.log.json. So the NodeModules folder is containing all of the dependencies that Cypress uses. It also contains Cypress with everything that's inside. Basically, a lot of stuff is in here, so don't forget to put your NodeModules into git ignore file so you don't commit that to the repository. That's a rookie mistake, but no one in here is that newbie, right? No one ever did this, not me, yeah. All right, just yeah, don't forget about it. Package.packagelog.json is basically the same thing as package.json. It just continues a lot more information. This is basically for your computer to read to resolve all of the dependencies and peer dependencies and stuff like that. So we have no need to do anything with that as for computers to read.
All right, we installed the Cypress. We initialized our project. Let's run Cypress. So to run Cypress, we get two commands. We get npx cypress run, which is a headless mode which will run all of our tests that we have in the project. We don't have any files in our project. So what we will do is to do npx cypress open, which will open Cypress GUI or Cypress graphic user interface. And that will welcome us with this nice welcome screen. So we can decide what kind of testing we want to do here. And since we are on an end to end workshop we'll pick end to end testing. And basically what Cypress is going to do on this step is going to create a set of configuration files. So it's going to create this Cypress config.js, some Cypress support, e2e files, some commands files and fixtures files. We'll go through those in a second. But basically this is what Cypress did for us. So when I continue, I can now go ahead and choose a browser. Now Cypress will take a look into my system and try to find all of the browsers that are supported and to basically display them here. Recently, Cypress has added a support for a WebKit browser. But since that's still in sort of an experimental mode, you need to do a little extra step there. You need to install the WebKit. Fun fact, it's actually the same WebKit that Playwright used. So you are actually going to do like NPM install Playwright web kit or something like that. And then you can go ahead and test in WebKit. But I usually like to choose Chrome. That's where I feel at home. Oh, look at me, I'm rhyming. All right. And yeah, when I do that, my Chrome browser is going to open and I have this nice UI in here where I can either scaffold example spec files or create an empty spec if I want. And plus I have this settings tab where I have all of these settings for the project. Basically, it's now set to default. And I have runs, which is a new UI that you can connect to a Cypress dashboard. So Cypress dashboard is a service, is a great reporter and analytics over your tests. So you can connect that here, but not going to go too deep into that. So let's start with scaffolding some examples specs. So if you choose this, Cypress is basically going to create a couple of test files for you and here they are now. So if I click on any of these, Cypress is going to run a test, basically open that file and run all of the tests that are inside there. So let me make this a little bit bigger. It's ran all in three seconds.
5. Cypress Project Basics
We rerun Cypress to see it in action. The project structure includes cypress-config.js, the Cypress folder with E2E, fixtures, and support subfolders. The E2E folder contains test files, fixtures stores static data, and support includes external modules or plugins. Chapter one focuses on creating the first test, covering Cypress syntax, URL setup, app capabilities, interaction methods, and background checks. The demo requires two terminals: one for running the application and another for opening Cypress. End-to-end testing with Chrome is demonstrated.
So if I rerun it again, you can see Cypress interacting with this to-do application. If I want, I can go and open another test and you can see these tests running. Oh, is that going to fail? Oh, we got a failing test. So yeah. This is sort of like a demo of the capabilities of Cypress. I'm not sure why that test failed. Maybe I have clicked inside the browser or something. Yeah, I bet I did. So yeah, this is how you can see Cypress in action. And yeah, that's about it. Let's go back to the VS code.
So in our VS code, we got two new items since we last saw it. We got the cypress-config.js and we got a cypress-folder over here. So the cypress-config.js is a configuration file. So basically it follows standards of web development. If you have ever worked with, I don't know, with Veed or Tailwind or whatever, there's this sort of convention of having a something config.js or.ts file where all your configuration is. With version 10, Cypress sort of changed that to follow exactly these convention before it was like a JSON file where you would have your configuration. And yep, this is how it looks. This is what's inside there by default. And you can see there's not much, which means that we don't really need to configure much in order to run Cypress, but if we did, we could put stuff like base URL here to tell Cypress, which is the URL of the application we want to test, there are things like changing the viewport height, viewport width. So all of the defaults, all of the settings would be inside this file. So that's CypressConfig.js and we got a Cypress Folder. And the folder has a very simple structure. We got the E2E folder, where you guessed it, we would have all of our test files. So you can actually see the to do, CY, and the action CY that we ran inside our, inside our Chrome browser. So all of the files that are inside the E2E folder will be displayed over here. That's like the default folder where Cypress looks for finding tests. You can of course change that by the way. And you would change that into cypressconfig.js. All right, so we got two more folders. One is fixtures where all your static data would be. So if you have like JSON files or images or stuff you want to upload during tests, et cetera, this is where they would go usually. And also we got support folder and we got two folders. So this is like a project scaffold. It doesn't contain too much. Basically this E2E JS contains an import of the commands file that's over here. And these commands file doesn't include anything just a couple of comments to basically show you what the file is supposed to be doing. The E2E file, which is often referred to as the support file basically it is loaded before all of your tests run. So if you have like an external module or a plug-in or something like that, that you want to use, you would add it in here. If we have enough time, we will be talking about plugins and I'm going to show you how to install one. All right, so that's basically it. Hopefully I went through all of the basics of Cypress project. This is how it looks. This is how you would start from scratch. Do you have any questions at this point? And we get silence. All right, silence is okay. Again, if you are feeling shy and you don't want to unmute your question, don't unmute your microphone to ask your question, you can use Slido to ask anonymously or I'm taking a look into the meeting chat as well as the Discord. So feel free to ask there if you want. All right, so that's it for the first chapter or rather for chapter zero because I want to call the chapter one the one where you would actually do something and try to code on your own and that's the creating first test. So you will learn how to create your first test or what kind of syntax does Cypress use? How to set up URL for the page you want to test? What are the capabilities of Cypress app? So we'll take a look at all of that. How to click, type, interact with our application and also what kind of checks does Cypress perform in the background? So we'll go through a lot of basics but after we go through those, we will start coding. All right, so my screen is doing something funny. All right, let's go into the demo. I'm going to open my Cypress project over here. And let's, I'll delete the folder. We'll not be using that anymore. And yes, so during the workshop I want to show you now if you want to go through the practical challenge, we will be, you will need to have two terminals opened. So in the first terminal, you will go and type npm start and this will spin up the application. The application we want to test, the Trello app that I've shown you, and you keep this running and then you open another terminal. And inside here, you're going to be opening Cypress. So npx cypress open. That's probably not the best way to see that. We got it. All right, npx cypress open. So we got the npm start, the application running. Another one will be npx cypress open to open our Cypress. And that's those two things you need to have opened in order to do the challenges, etc. So I'll go ahead and choose end-to-end testing. I'll choose Chrome, because that's my favorite and I'll arrange my windows a little bit. And let's go to the demo start.
6. Writing Tests with Cypress
We have two tests in Cypress, each represented by an it block. The it function takes a test name and a callback containing Cypress commands. The visit command is the easiest to use, with the syntax CY.visit(URL). The base URL can be set in cypressconfig.js, allowing for shorter visit commands. Saving the file triggers automatic rerunning of the test, providing a fast feedback loop.
All right, so I got two tests in here and let me actually open these demo start file in the VSCode as well. So here I have it opened. And yeah, let me walk you through what's going on here because there's a lot. So we have two tests in here. So we got this first. It creates a list with a card in it and it bookmarks a board. And you can see the names of those tests being reflected in the Cypress UI, right, creates a new list, bookmarks a board. So in Cypress, there are many tools that are bundled inside Cypress, and one of them is Mocha. So if you have ever worked with Mocha, you probably are already familiar with what's going on in here. If you haven't been using it, basically there is this it function. And this it function takes two parameters. The first one is going to be name of the test. And the second one is a callback. And that's going to contain all of the commands, all of the Cypress commands that we want Cypress to execute. So basically one it block means one test. So if I have like multiple it blocks, I save it, I will have multiple tests. They're all empty now, so Cypress is not doing anything. I just wanted to show you that one it blocks equals one test. All right. So let's write our first test. I'm going to put this back, those two tests that I had in here. And if I want to have just one of those tests running, I can do it dot only, and it's going to only run the first test. Let me save this. And now we can see it is running only the first test. All right, so let's now back up a little because I already have a command in here and it may not be, although the syntax is pretty readable, but it may not be totally clear what I'm doing here. So the first command I usually teach on my workshop is visit command because it's sort of the easiest one, right? So if I do CY visit and tell the application which URL I want to visit, it looks like this, right? So we have three parts of here. This is the CY and that's the GlobalCypress object. So you don't have to import anything, you don't need to do anything extra. You just have that CY in every file that you create. When you use the CY, you have all of the functions inside here. So CY.visit, the visit is the function that's inside the global object. So we go CY visits, CY click, CY get, CY, all of the different commands. This is basically the syntax you will be using throughout all of the tests. So CY from the globalCy object, I want to use the visit function. Now, the localhost 3000 that I have over here is basically an argument of that visit function. So if I do CY visit http localhost 3000, I'm basically telling Cypress to visit the location. Localhost 3000. So let me save that. And you can see now that I'm opening the location 3000. Now, you might have seen that before. I just had a slash in here, right? So just visit slash. Now, why did I have that here? Well, the reason is very simple. If I go ahead and go to my cypressconfig.js and I register my base URL to be the localhost 3000, then all I need to do is to do visit slash. And it's basically going to append whatever I have in my visit command to my base URL. So if I go back and do visit slash, it's basically going to open my homepage. If I want to open the detail of the boards, like this things to buy board, I can't really see that over here, but the full URL is localhost 3000 slash board slash one. So, if I want to visit this location instead, I can go slash board, slash one and it's going to visit the board directly. So instead of going to this home screen, we are landing right inside the detail. We could go slash log in slash sign up if we wanted to. So yeah, that's, that's how it works. All right, so now that we have visited our application, let's, let's do something. Let's actually write a test. Oh, I totally forgot to mention one thing you might have already noticed, but whenever I hit command S and save my file, Cypress is watching for the changes in, in that file, in the one that it's currently running. So if I decided to change something and save it, is automatically going to rerun, which is, which is great. It makes for a really fast feedback loop. And I use this a lot, basically, to see what my test is doing at all times. So whenever I add a new command, I can see, see what's happening. Now, yeah, I think, I think that's, that's all I wanted to say on, on that part.
7. Automating Trello Board and Element Selection
We automate a simple scenario in which we create a new list and a new card on our Trello board. To select elements, we use the CY get command, which allows us to use CSS query syntax. We can select elements by ID, class, or other CSS selectors. Cypress also provides a selector playground tool that helps us select elements on the page. To type into an element, we use the type command, and we can even simulate hitting the Enter key. The Cypress documentation is a valuable resource that provides detailed information and examples for each command. In our test, we select the add another card element and click on it to create a new card.
All right, so let's, let's write an automation. We want to, we have our Trello board. So how about we automate a very simple scenario. We go to this enter list style, we create a new list, and then we click on add another card, and we create a new card. That will be a scenario simple enough. So, so let's do that.
Now, how do we do that? Let me rerun my test, it's going to reset the database. So I want to go ahead and type into this field. Now for getting a element out of our page, we go, we have a command that's called CY get. Now the CY get is actually, we don't have to define whether we want to select an element by ID or by class or by something else. We use the same query syntax that is used in CSS. So if you have ever written a CSS, then you might be already familiar with that. If you want to select an element that has an ID, you go hash an element with that ID, right? So element, so that's how you would select an element with an ID of my element. If you have a class, then you pre-append it by dot. So dot, my element, it's element with a class called MyElement. And you can also do all kinds of crazy selects, if you want to select a child element of that element or something like that, you can all do that. So whatever CSS selectors allow you to do, you can do that with the get command, plus there are so many helper commands that you can use they can just get to pretty much into anything. Just recently I had a debate with someone on Twitter that they have to use expats. Arguing they might not need them, but I don't really know the situation fully, but the main advantage of like expat is that you can select like a sibling element. Like the next one or the previous one. You can find the child element or you can find the parent element, but in Cypress you can do all of that. So you get your element and then you append another command basically to select a previous element or the next element. Or if you want to find children elements, you can find those or you can find a parent element and you can even specify what kind of element that should be. So a parent element with a class of parent or something like that. So there's as far as the selecting goes, you can go pretty much anywhere with that. But there's another really, really useful tool you can use in Cypress and that's called selector playground. The selector playground is actually over here in inside the browser. This little icon basically helps you to select any element on the page in the same way that you could do with the elements panel inside Chrome DevTools. So if I click on that, it actually is going to generate the whole command and I can just copy that to clipboard and paste it into my test and voila, I now have a selector exactly the one that I need. Yeah, so let me save that. And now I'm selecting that element. It's actually a little bit scrawled here, but if I hover over, I can see the element being selected and highlighted, not sure how well you see that, but it is highlighted over here. And yeah, so let's go back to the scenario we wanted to test, right? We want to create a new list and then a new cart. So, what I'm going to do, since I already got the element, I want to type into it. So let me type, I don't know, groceries list. And I'll type it in, save my test. And as you can see, I'm already typing in the groceries list, which is good. So the next step, I actually have two options. I can either go and click on this add list button, which is going to add it, or delete this and hit Enter key, which does the same thing. So, what if I want to hit Enter key within this type command? So, it's actually pretty easy. I just go ahead and put curly brackets in here and type in Enter. When I save this, you can see that it types in the groceries list, and then hits Enter, and that's going to create my list over here. Now, how do I know to do that? I'm glad you asked. It's all in the documentation. And I know everyone is going to tell you, read the documentation, but I would really, really like you to read the documentation, because it's actually pretty great. Now, the one thing I haven't talked about is this reference types Cypress over here, which is basically a special kind of comment in my code, which basically tells VS code to look for types from Cypress namespace. What that means translated to human language is that when I start typing C, Y and dot, it's actually going to autosuggest these commands for me. Now, without this, it wouldn't do that, and besides giving me the autocomplete, when I hover over any of the commands is going to give me this small documentation for every command. And if I hover over the type command, you can also see that I have this C Y, I get input type hello and hit enter. So I have the information in here, but if I go and copy this link, I also have the link right into the documentation. So when I do that, I can see what's in here. What does this command do? Types into a DOM element. I can see the syntax. I see the correct usage. I'll see the incorrect usage. And I also see all of the special characters I can write in with the type command. So, yeah, besides that, Cypress has a lot of examples in here, a lot of small code snippets that you can see, like how would my command interact with that, et cetera. It's really good. I learned so much from this. And, yeah, I definitely recommend reading this and taking a look into it and working with that because, yeah, the workshop is going to end in like two hours, but documentation is forever. So use that.
All right, back to our test. So we already hit the groceries list. So we already know what's going to be next. We want to select these add another card elements. So let's copy that, paste it in here, and we want to click on that. So how hard should that be? Click. That's it. That's all we need to do. So when we save our tests, see what it does now? You can see that we have created the groceries list and now we clicked on add another card and we see this input field in here.
8. Automating Trello Board and Element Selection
So let's again go ahead selected that, copy the clipboard, paste it into our test. And now I want to again, use type to add something into our groceries list. So let's buy some bread. Hit enter key. And our test is complete. Awesome, so let's now move on to the second test because there's one thing I would like to show you.
9. Using Plugins and Selecting Elements
There is a plugin that uses the Chrome dev tools protocol to trigger events, similar to Selenium 4. Participants are encouraged to start coding using the provided demo and challenges. A question is asked about rewriting a test to select elements based on their sibling element. The speaker provides guidance on using the get command and the contains command to achieve this. Participants are given time to code along and complete the challenges. The next chapter on simple assertions is introduced, and the demo is set up to explore the should command, before each hook, and various assertion techniques.
10. Testing Cart Creation and Deletion
There's also an after hook and after each hook. It's better to start with a clean state instead of ending with one. The hanging state of tests can be beneficial for interaction. Before each hook works better than after each hooks. In the first test, we create a new cart and assert its visibility. In the second test, we check the proper number of cards using the should have length assertion. In the next test, we delete a card and check the checkbox state.
And there's also a after hook and also after each. So if you want to do that maybe to like wipe your data or something like that, you can do that. I would in general advice you to start with the clean state instead of ending with the clean state. The reason for that is that the hanging state of your tests might actually be a good thing for you because you might want to keep interacting with your application as you're creating that test. If you delete all of the data and you try to do something then your API's won't work, et cetera. It's actually a good thing. And also if something in the hook does not work it's much better to have that not work before the test rather than after test. Because if it happens after test it might actually sort of overflow to the next test. And if you have test number one, test number two and your test number two is failing the reason might actually be test number one if you're using after each hook. So that's something maybe to consider. In general, before each hook worked much better for me than doing after each hooks.
All right, so let's jump into the first test. In the first test, we want to create a new cart and what's going on here? I don't have a list in here, so let's create new list. Hit enter key and when I say my tests, now I think we should be okay. Yeah, we are, all right. So this test creates a cart. It's going to click on that new cart button and then it's going to type in the bread. Let me make it a little bit smaller. I think that's still pretty readable and doesn't wrap my text. So yeah, we created a new cart and like we do the automation but we want to make a test, right? You could argue we did not test anything yet. We're just interacting with the application. I think that there would be some merit to that critique because yeah, we interacted with the application but looking at the test, we have no way of knowing that the cart was actually created. What happens when we hit that enter key? There's actually a cart that's in here. We can see that. So we should be able to tell that it is visible. So I'm going to select this cart and write an assertion. And to write an assertion, there's this nice command called should. And this should command is going to give us all of the assertions that are inside Cypress. There's quite a lot of them as you can see. So we can search through them. So we're trying to find something that's visible and there's a be visible assertion. So let's do that. And wala. We now have asserted that actually both of our breadth elements are visible. So again, this is something that you might consider thinking about when I don't clean up the state of the application, then I'm getting into trouble. I have two elements instead of one.
All right, so let's jump into our second test and that's going to be checking whether we have the proper number of cards. So I copied a bunch of code from here and I'm going to paste it inside here. Oh, I didn't copy that. Oh, let's do that then. Let's again, create a card. And when I create that I want to make sure that there is a certain number of cards. Let's actually create a second one. So we will have two. Let's actually start with one and we create another and then we make an assertion. So I'm going to again, select my card. And I want to make sure that at the end of this test run, we will have two cards. So let's again use should. And the way I'm going to do that is to check the half length of two. So when I save this, it's going to pass. So I have two cards and I'm asserting that there are two of them. Now you might be asking like why we have such a weird assertion here? Why is it half length of two? Why not have count? Why does it doesn't have something with counting and we have a length instead? Well, the reason for that is that whenever we use a get command and that get command finds multiple elements. Cypress is actually going to put them all in an array. And we can see that when we take a look into the console and take a look into the details. So I clicked on the command. It has printed out the information into the console. And I can see this yield attribute over here that's telling me that there are two elements and they are in an array. So here they are. We've got two elements. So what I'm doing here in my assertion with the should have length two is that I'm asserting the length of that array. So how many elements were found in the DOM. So yeah, that's why we have these have length.
All right, let's go into the next test. And in this test, I'm going to delete one of these cards. And what I'm going to do here is I'm going to check a checkbox and then make sure that it has the proper state. So let's do, cya.card.checkbox. I'm going to use click command to check the checkbox. And what I do, you can see that it is checked and I have this green background behind the date over here. Now, I'm going to give you a little tip here. If I run this test multiple times, you can see that I'm always ending in a different state.
11. Checking Checkbox State and Testing Repeatability
So instead of using the click command, we can use a check command to ensure the checkbox is always checked. We can also write assertions to check if the checkbox is checked and if a certain class is added to the element. When checking the text of an element, we need to use the have value assertion instead of have text for input elements. To ensure tests can be executed repeatedly, we can wipe out the database and seed it with correct data, use API calls to create the necessary resources, or use page objects to set up the desired state before testing. The application in the demo uses an HTTP request to reset the application, but this may not be available in other cases.
So now the check box is unchecked. Now it is checked. If I run it again, it was back. If I run it again, it's checked again. So instead of using the click command, what we can do, we can use a check command. And this will make sure that whenever we run the test, we will end up in a checked state. You might want to be in that situation, you might not want to be in that situation, but check box, the check command actually works for check boxes and radio buttons. So if you absolutely want to end in a checked state, the check command is the better way to go.
So no matter how many times I run the test, it's always going to end up in the checked state. Now there are two things that happen after we check the check box. The first one is that the check box is checked. So we can write an assertion, shoot, search for a checked, we got a B checked assertion. So that's one thing that's passing, that's awesome. And the second thing is over here. When I take a look, let me actually open the InSpecs element panel, now I highlight this due date element, it's over here, it's highlighted. And as I start interacting with my check box, you can see right about here, we have a class completed and when I uncheck, my check box is going to disappear. So as I'm interacting with that, it's either appearing or disappearing. So this is something that gets added to my element when I check it. So let's select my element and I want to make sure that the class is added in there. So let's do shoot. And I want to make sure that it has a certain class. So it should have a class and that class name is called Completed. Be aware, I'm not typing dot Completed because I'm asserting the name of the class. So I'm not like, the dot Completed would be like when I want to select something, but this is actually just the name of the class. That's how the class is called. Should have class Completed would then be the assertion. So let's save it. And while I'm making sure that the checkbox is in correct state, and also making sure that the class is, that the element has a certain class.
All right, one more test I want to show you, then you can go ahead and start coding. And the element we have a correct list name. So we want to make sure that this list name has a correct name. So let's go ahead and check the text of that element. So we select it, data c y list name and I'm making assertion should have text of a new list. Right? Let's save it and see what's happening. My test is actually failing. So why is that? Well, you can see the error that it says, we wanted to have a text new list, but the text was empty. So what's going on here? We can clearly see the list. So why is that? Now, if we take a look into, through the inspect element and take a look into our element, we can see that this is actually a input element. And we don't really see the text, the new list text inside here. And that is because input is an HTML5 element that actually holds the user input. So it actually holds a value. And although we can see that, we can see it as a text, it's not a text, it's a value of that element. So it's different from a heading element or a paragraph element or div element, which would have an opening and closing tag, and a text between the inputs element does not have an opening and closing tag. It's just an input element. And it contains that value that is inputted by the user. So since this application is using these inputs to sort of show the name of the list, the correct assertion would not be have text but to have a value. And now it's actually passing. So the situation would be different compared to, for example, this bred element where we can see that this is a div element, right? So this is a div and we can see the text bred being over here. So in this case we would use have text but in case of input or something else that holds value we would have a have value.
I have a question from Sebastian. He says, can you show us how you ensure that your tests can be executed repeatedly, for example executing smoke tests really multiple times during the day. Maybe there is something sometime later to clarify this. Well, the way there are like multiple ways and this is really like not really like Cypress question but it is a question of like how to architect our tests. So they are repeatable and when you run them in parallel they don't interfere with one another, et cetera. And there's a lot that could be said into that. So I'm not really going into that but what I like to do is like in some of the tests I mentioned that in the beginning I have this script that will wipe out the database and seed it with correct data. So seeding the database would be one of the ways of doing that, making sure that before you start your test you have the proper data insight. Another one would be using API calls to sort of create the information the resources you want for that test and then open your front end and interact with the created data. So that might be something a way of doing that. And other way would be like using page objects and just creating the stuff you want through the UI before you start testing the thing you want. It's not the ideal way, just using UI for everything but sometimes you're just limited to that. So yeah, that would be like my short answer to a very complex question. And obviously there are like multiple strategies of how you can do that. What I do inside this application, I have this so I can send a request like this sort of going ahead, but I have a post API reset and that's basically doing the same thing. I'm sending an HTTP request to reset the application. And when I hit F2 and do like click on this button it's basically doing the same. It's just going to reset the whole database. But yeah, this is just the playground application. You're not going to have a HTTP request like this available to you. But yeah, I would put whatever I need to do in the before each and then make sure that these tests actually are dependent on that before each actions happening.
12. Chaining Commands and Dual Commands
In this chapter, we will cover the concepts of chaining commands in Cypress, built-in re-triability, and writing effective commands in chains. We will also explore how to write tests for flaky applications. The demo will focus on finding a card with a due date on the first of March using the contains command. We will also discuss the different types of commands in Cypress, including parent commands, child commands, and dual commands. Chaining commands will be demonstrated using the contains command with different contexts. The console will be used to visualize the behavior of the commands.
And that's like the state where they want to start. So here I could have like resetting the database or setting up the stage for something, basically, if you know like the arrange act assert structure for your test, then in the before each, I would have the whole arrange part, and then the act would be starting in the eat block with the visit command or something like that. And then there would be assertion or a couple of assertions actually. So yeah, thank you for that question.
All right, we got a couple of challenges in this chapter as well. So I would like you to try them out. I would also like to have a small break. So let's maybe merge that into one thing, and we can meet in like 15 minutes again, and then we'll continue on with the rest of the workshop if that's okay. Hope you had some refreshments. Maybe you got to code a little bit as well. So we can now jump into the next couple of chapters.
And one of the like the very core thing that I like to teach on my workshop is this chaining and re-triability thing that's going on in Cypress. It's actually a really important thing, which even if you're reading the documentation, sometimes gets missed. And it's actually very important for you to be able to write stable tests and to make sure that they don't become flaky. So, there are a couple of principles I would like to explain in this chapter. Now, one thing would be how chaining commands in Cypress works. Also the built in re-triability and how to write effective commands in chains and make sure how to write a test if your application is actually flaky. So, let's jump into the demo and yeah, let's do the demo, chaining and demo start. Alright, again, I got three tests in here. So I'll start with the first one, one two demo start. And here we have it. Alright, so I got this shopping list. You can see I'm kind of late on my shopping. It's already end of the year and I got items from March and February in here. So, let me explain a couple of concepts here and the first concept that I would like to explain is chaining. So, let's go ahead and try to find a card with a due date on first March. So, what I can do, I already shown you in the previous example, I can use, if I have a text, I can go and do contends. So, if I select an element that contains the text March 02, 2022, then it's going to find an element and I'm not sure how well that is, how well that you can see, but it has found this element inside here. Here's the highlights. I'm sure how well that's visible. But yeah, this is the one. So, when I take a closer look into my application, I can see that there's another one that has the March 01, 2022. So, why does the contains only select only one? Well, the answer is simple, that's how the contains command works. Compared to the get command, get command is going to just find all, find and select all of the elements that are basically are the same as the selector that we have given, right? So, if we select a div, it will find all the divs. If we select something with the class, it will find all the elements with the class, with a certain class. With cy-contains, it's actually only going to find the first element within the context. Now, what do I mean within the context? There is a very important concept in Cypress called chaining. And when we're talking about chaining, we have three types of commands in Cypress. So, the first type is a parent command, second type is a child command, and the third type is a dual command. So, we already have seen all of them in action. So, an example of a parent command would be this cyvisits. So, whenever we use a parent command, it's going to be a cy and then something, the name of the command. Then we have a child commands. So, a typical child command would be a click. If we want to click on something, we can't just go see why click. That's not how Cypress works. It's not going to just click anywhere. It needs an element. Before that, we need to first do get or contains or basically select an element and then we can click on it, right? So, the click command, that would be an example of a child command. Now, the contains command is actually an example of a dual command. This one is going to behave differently based on whether it is chained of the Cy object or if it's chained of some different element. So, let me show you that in action. So, I have contains March 01. Let me duplicate that, but with the second one, we are going to select this second element and the way I'm going to do that is by utilizing chaining. So, first of all, I'm going to select my list. So, let's do data cy-list. And this is basically going to find two elements. It's going to find this first list and the second list. So, what I'm going to do is to do EQ one. Remember, we're numbering from zero, so this would be zero and this would be one. So, in this case, EQ one will select this element, and then I'm going to use the contains command. So, that's it. So, now when I save my test, although the contains has the, oops, has the same argument in here and in here, it will behave differently. So, if I hover over this first contains command, it's finding this element. If I hover over click on the second contains command, you can see that it's finding this element over here. So, the way that works can actually be very nicely revealed using the console. So, I'll open up the console and click on my get command. So, when I click on it, I can see that this get command with its nataseylist selector has found two elements. And we got it over here, we got this yielded parameter. And we see what the command has actually found and what it is actually passing onto the next command.
13. Cypress Command Chains and Retriability
Cypress command chains pass information from one command to another until an assertion or action is performed. Retriability allows for retrying commands and assertions for a specified period of time. Assertions and previous commands are retried until the specified condition is met. The timeout for commands and assertions can be adjusted at the command or test level. It is important to find a balance between longer retry times and test execution speed. When combining chaining and retriability, it is possible to encounter issues where a command is stuck in an endless loop. The assertion retries the previous command, but not the entire chain, resulting in an unchanged condition. The console can provide more insight into the behavior of commands.
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.
14. Chai Assertions and Chaining
15. Writing Assertions with Chai and Chaining Commands
Instead of filtering by EQ, use the then command with a callback function to write multiple assertions at once. When testing lists that render in one order and then snap into the correct order, change the then function to a should function for retrying assertions. The then function does not have a retry logic, but the should function does. The shoot command is a wrapper around chai assertions. The then command is advantageous when testing APIs, as it provides the response once it is received. Be mindful that the then command is not simply a shoot function without retryability. Use the then command when you want to retry assertions.
So instead of filtering them by EQ, I'm just going to use then command, then pass a callback and as a parameter, what I'm going to get is those elements. So I'm just going to call them cards and I'll write a couple of assertions. So let's do expect cards. And let me write a couple of assertions here. So let's do this expect cards to have text. And then we will add those texts. So that's, this will be zero, one, two, and then we'll have milk, bread and the juice. Right? So we basically have the same test. When I save it, it's going to do the same thing just with less code. So we can write multiple assertions at once, which is neat, right?
Now, I would like to show you one thing here. What happens if you have a list, but that list actually renders for split-second in one order and then it would just snap into the correct order a split-second after. That can happen. You've probably seen that happen. If not, then you're lucky. So I'm going to change this assertion and I'm going to say that the second element has the text to juice and the third element has the text bread, right? So this is not incorrect order or rather this assertion is not incorrect order with this one. So when I save my test, of course, as you would expect, it's going to fail. But the interesting thing about this one is, hey, look at the number over here. This is actually failing pretty fast. So that's like 599 milliseconds. So where are our four seconds, right? So why doesn't that retry? And the answer is not all the commands in Cypress actually have that retry logic and then function is actually one of them. So what do we do when we want to retry those assertions? Is that like not an option? Well, it is an option. We can do it like this. We can just change our then function into a should function. And this way, whatever is inside is going to get retried until it returns true, it's going to be retrying. So when I save this now, you'll see that it is sort of waiting for the right order to happen. Make it fail. Run it again. And as soon as the order becomes that way, it's going to pass. So now I have a passing test and a multiple assertions that are trying to run. And as soon as the code inside is returning true, it's passing. So this is nice. We are sort of testing multiple things at once just by changing that then command into shoot. So yeah, that was like the short demonstration of how you can write different kinds of assertions. So they're called chai assertions. And the shoot command again is just like a wrapper around those. So yeah, is there advantage to use then instead of shoot? Yes, there is. If you are testing APIs, because when you are testing APIs, when you use request, basically the thing we're just going to be talking about in a second, then once the response will come, you just have that response. So having shoot with a four-second retry makes no sense. Because if you would be, you would wait for something to change, for something that will never change. You will be waiting for something that will never change to change. That's a weird sentence, but yeah, that's what it is. So you might be in a situation where you just want to use that then instead of shoot. Just be mindful, then is like not a, it's not simply a shoot function without a retryability. So you cannot do like, then, and then have like, be visible or something like that. That doesn't work. It's not like one to one. It's a different function. It's just that you can use shoot with a call back function that can be retried. So yeah.
16. HTTP Requests and Testing
In this part, we learn how to send an HTTP request, test the response, set up attributes, and reset the Trello application. We use the cy.request command to send an HTTP request and observe the requests made by the application. By mimicking the behavior of the application, we can create a board using an API. We can then test the response and its attributes, such as the status code. The difference between the then and should commands is explained, with recommendations for using a timeout of zero for cy.request tests. The next part focuses on testing the board list by retrieving data from the database using the request command.
All right, let's, I totally forgot about my presentation, by the way. All right, so let's move on to the next one. That's HTTP requests. By the way, keep those questions coming. I love that you are asking.
All right, so HTTP requests. So, we will learn how to send an HTTP request, how to test the response, how to set up different attributes, and how to reset the Trello application, the one that we are testing. So let's do some testing. Demo, and demo.
All right. So I'll filter the first one. Let's save it. And I'm doing basically nothing in this test. I'm just using the cyvisit to open my application. So what I can do in Cypress is to use this neat command called cy.request. And it's going to send an HTTP request. So for example, if I want to create a new board using requests, the first thing I would do, I would look into the documentation. But I know that's not always the case. Not everyone writes the documentation. But yeah, that happens. What we can do, is we can learn about the application and what it is doing by observing what it's doing. So if I go ahead and do create a new board, and create a new board, click on the button, I can actually see the HTTP requests happening in here. So I can see that as soon as I hit that Enter key or click on the Create board, there's a POST request to the API boards that is creating that request. And if I take a look into the detail, I'll see the response body. I don't see the request body, not sure why, but yeah, it's somewhere there.
So what we can do, we can mimic that behavior. We can try to create our board by using an API. So let's do request, and what I'm going to do here is I'm going to use the method of POST. I'm going to use the URL of API boards just as the one that we had in here. And then I'm going to pass a third argument into our request command, and that will be the body. So that will be the object that we want to send towards the server. And since I know the application, because I made it, I know that there's a name attribute, which will be the name of our board. So that will be board created by API. So when I save my test now, I can see that my board created by API is over here. And the reason for that is that I've sent the request. So here's my request command, here's my visit command. So when I click on the request and take a look into the details, I can actually see the details of that request. So I'll see the request body, I'll see the headers, the response, the status. And I already have my yielded attribute in here. And you know what that means. We can just test anything that's in here. So we could use the then function that we just used. That will work. We'll write a couple of assertions, etc. But we can also do something like this. We can just go ahead and use an its command. And I really like this one because it's just going to take a look into this object and just take whatever attribute we need. So for example, we can take a look into its body. Or no, you know what? Let's take a look at inside the status. So let's take a look at the status and we see the status is 201. So let's go with the shoot and it should equal to 201. And I can just delete this visit and we just have a pure API test in here now. So we are checking the status and it equals to 201. By the way, I just had the question, right? What's the difference between then and should. Now I said that should command has retry ability and it's going to retry. So it doesn't really make sense to use it with cyrequest and I stand by it. So in case you are doing something like this I would recommend to use a timeout of zero so it doesn't retry this. And if I put something like this err to 00 in here it would fail immediately. If I didn't have the timeout in here and save it it will try to retry it and make sure that the and we would attempt to test that the 201 eventually becomes 200 but that's just not going to happen. So yeah, this is a trick for achieving that but in this case this is like a very, very simple test. If you just want to make sure that that the test is like really simple just want to check the status code or something you can do it like this. It's sort of a short way of testing your API but if you want to test something more complex then I would recommend to do like the full using the then command.
All right, so let's make sure that the response gets 201 status. I'm wondering if I'm showing anything new in here. In this case, let me take a look into my... Yeah, I kind of already shown this, let me jump into the next test instead. Testing board list. All right, so in here I want to instead of posting some data to the database, I want to get some data from the database. So I'm going to use request and I've shown you these simple syntax where you would have three arguments basically, right? So the first one would be the method, the second one would be the URL and the third one starting here would be the body, right? Instead of passing arguments like that, we can be more complex with our request. So I'm going to pass an object and in that object I'm going to define everything.
17. Intercepting Requests and Testing
In this part, we learn how to send an HTTP request, test the response, set up attributes, and reset the Trello application. We use the cy.request command to send an HTTP request and observe the requests made by the application. By mimicking the behavior of the application, we can create a board using an API. We can then test the response and its attributes, such as the status code. The usage of the request command is useful for preparing the application and opening it in the desired state for testing. Instead of using UI for setting up the test environment, API calls can be used. The intercept command allows us to watch for API requests made by the application and write tests for them. By adding an alias to the intercept command, we can wait for specific API calls to happen and check that they are being made.
So the method in this case will be get, the URL will be API boards and we're not sending anybody in here so let's just keep it that way. So when I save it going to return 200 but there's a catch in this case and this is just very specific to my application so this is not like it applies everywhere but in my application, when I'm using a get command and I'm not specifying what kind of answer I want from the server, it's going to respond with an HTML. So you can see here's an HTML for the response body which is not something I want. In this case, I want to have response in a JSON format. So usually if you want to send some information to the server, it is headers where that would be so let's do headers and specified response body. So I will specify the headers property to have an accept and application JSON so this is the information. So in the headers you would usually have information like this, maybe you have your authorization token or something like that in there but yeah this is how you would specify that. So if I save my test now instead of getting that HTML I will have the response body. So I have the list of all of the boards that I have created so far. You can see I created quite a lot of board created via API boards as I was shoving this test. So yeah this is all of the boards in my database and what I can now do is write an assertion, so let's go with then and I'll go with board and I will write a couple of assertions, so let's do expect and I want to board status to equal and want that to be 200. So here I have the status check. I want to make sure that the board body has length of, how much is it? Seven, right? And stuff like that. Like, you can do all kinds of testing. By the way, just a shameless plug here, I do have an API workshop coming, so if you are interested in that, I think you should sign up and I'm showing off like different ways of how you can test APIs in using Cypress. All right, one more thing I want to show you. I already kind of shown you this, but there's an API for me to reset the database. So I'll go request-post-api-reset. And when I save it, it's going to reset my database. But instead of having this as a test, what I can do is to use this as a before, oops. So in this way, when I save my test, it's failing and I believe it's failing because one of the assertions in here and it has a length of one instead of two. So yeah, the tests are kind of bleeding one into another. So that's why it is failing now. But yeah, I have this before hook, which cleans up the state and then I have couple of tests running within this spec. So usage of the request command is actually very useful exactly for this purpose, where you can just use a bunch of API calls to prepare your application and then open your application exactly where you want it to have it opened. So I know that there's a convention of like using page objects basically to set up a some sort of state where you would go through different steps to basically get to the point of interest to the one you actually want to test. With the, how would I say it, my personal strategy is although we are in UI tests, I would advise to use UI as little as possible. If you can avoid using UI, avoid that, just use UI for testing the user story you are interested in. So if you have 100 tests, you don't want to log in 100 times, you just want to have one test that tests the login and then all of those other tests, you just open up your application in a logged-in state. If I have a board, I don't want to create a new board before every test using UI, I just call an API to create my board and then open my test at the board's detail because that's what I want to test. And requests can be really, really helpful for that just sort of setting up the stage for your test. So yeah, that's HTTP requests. Feel free to shoot any questions. I'm going to grab water. All right. Looks like we will actually make it. So I'm going to show you probably the most fun part of Cypress now. So this is the intercepting requests. So we're to show you how to watch for API requests that our application makes, how to test those API requests and how to stabilize a flaky test. And I'm going to show you different ways of matching requests. So what do I mean by intercept? Well, let me show you. Demo start. Let's open the demo, start here as well. Awesome. Okay. So The board has no lists, no I actually want this first test. All right, so in this test, I have a scenario we have seen a couple of times already, right? We're creating a new cart. So in this new cart, what happens when I create a new cart? If we are focusing on APIs, you can see that there's this POST API carts being called. Now what I can do in Cypress is sort of combine to UI aspect of my testing with the API. And let me show you what I mean by that. I'm going to type a new command here. That's called intercept. The syntax is actually kind of similar to what we have in our request in a way that we have the first thing then where we first parameter, where we pass in the method and the second one where we pass the URL. So I used the command and you can see that there is this like no alias thing in here. And there's also this routes table which is going to tell me like what are the API requests that Cypress is now watching for? So this is what the intercept command does. What the intercept command actually does is that it tells Cypress to watch for certain API calls to happen. So instead of sending those. The big difference between request and intercept because I'm saying that because sometimes it confuses people but with requests, I am the one that's sending that API request, right? Sort of like Postman or something like that. With intercept, I'm not sending anything. I'm just interacting with my application and my application is actually doing the HTTP calls. So when I'm interacting with my app and I click on add cart, click on logout, click on login, click on whatever, there are HTTP requests being called. With intercept, I can watch those request happen and then actually write a test for them. So what I'm going to do here with this intercept command is that I'm going to add an alias. So let's do alias and I'll do, create cart. That's what I'm going to call my request. When I save it now, run my test again, you see this nice little alias happening over here. So what I can do now is to wait for that alias to happen. So create card. So what I did in this test is now that not only I'm interacting with the application, but I'm actually expecting an HTTP call being made, right? So I could easily have a bug where I would click on something and it will not send anything to the database. Well, with this one, I'm checking that it actually does send something to the database.
18. Combining UI and API Tests
When combining UI tests with API tests, the intercept command is useful. It allows us to wait for API responses before making assertions. We can use regular expressions or mini-match syntax to match specific requests. In cases where elements load asynchronously, we can use the contains command to wait for specific elements to appear and disappear. It is important to ensure that positive assertions are made before negative assertions. Combining UI and API tests can provide a comprehensive testing approach.
And when I click on these wait commands and take a look in the console in the detail. Again, I have this yielded attribute and I see everything that's going on with this request. So I can test the response. I see the status code. I see the body of that response. And I even send a request. So what I can actually take a look into is that whether the front end, when a user types in some kind of text, whether it is not like modified in some undesired way. So I can check both the request and the response. So let's maybe show that in a very simple test. Let's go for, let's go for, oh, let's just do a simple check. Let's make sure that it's a response status code equals 201. Right, so what I did here is I just combined a UI test with an end-to-end test. So I'm using with an API test. So I'm using my UI to interact with my application and then I'm testing whether the API is working correctly. So yeah, let's move on to the next test.
So in this one, I'm going to show you a different power of our test, of our intercept command. So in this test, I have a, in this test I want to make sure that the board has no lists inside it. So I have an assertion over here, get data list, and it should not exist. But what's happening here is that we actually have a list in here. So why is that? What's going on? If I take a look into the timeline, I can see that my GET assertion is actually being done at the time while my lists or my data is still loading. So I'm getting a false positive here, right? And that's no good. So what I need to do is actually wait for that API lists to give me a response so that I'll make sure that everything is loaded and then make my assertion. So I can do that with intercept. So let's do intercept get. And then if I want to match the lists and there are different strategies of how I can match that request. Since we have a lists and we have a query string of board ID equals to one we can just write the regular expression and match it to something like this lists. So if it contains the word lists and it is a method of get then we're going to match that and give it an alias of lists, right? So now when I matched it it's still passing as it should not but what I can do is I just go CY visit and wait for my list request to happen and when I now save my test you can see that it is actually failing as it should because it says we should not have any lists in our UI but we do have one so this should be failing. Now, we can of course use intercept for that but just let me give you like a side note about this one. Don't try to over complicate things. I maybe showed an example that could work but this is a simplified app. When I open the board it triggers three HTTP requests and I know exactly for which one I want to wait. Sometimes when I want to write a test and I have this situation where I get my element too soon, I can do it in a simpler way, right? So when my application is still loading, I have this loading data going on here. So instead of doing that I can just go CY contains loading data, should, well, first of all, I want to make sure that it is visible and then I want to make sure that it disappears. So do something like this. So the contains data element should not be visible, should be visible and then it should not be visible. And then we move on to our test, to getting our list and making sure that there are no list. So let me save that. And, ooh okay, it's not passing. Did I do something incorrectly? Loading data. I don't know. Well, I wanted to show you a simpler example and I probably just complicated things here. What I wanted to show is that you can just take an element that should not be there. I mean, contains loading data. It does contain something a little extra but I feel like this should be passing. Maybe there was a, okay, to be visible, not to be visible, I don't know. Well, maybe this is too much. Maybe I should just make sure that it is not visible but whenever I do a negative assertion, I want to make sure that I first do the positive assertion and then do the negative assertion. So this is like less than optimal and I'm not sure why. Oh, okay, I get it now. It's, I made the wrong assertion. It should be not exist and I think this might work. So I should exist and then should not. Hmm, okay. I'm not entirely happy with that, but this works. And I thought I'm going to show you something that's less complicated and I ended up showing you something that is more complicated. So I guess just using the intro set would be better way in this, but if you, I guess if you have the longer loading than selecting that loader and focusing on that to be appearing and then disappearing would make things easier if you have like 30 requests that are happening, they're all asynchronous, they all end up in different times and you just want to wait for the last one to finish so that you can go on and test your app because everything is loaded at that time. So yeah, all right. One more thing I wanted to show you. If you want to combine your UI tests with API tests, again, going to use this CY intercept and I can again, similarly to request pass an object. So that would be the method of delete because I'm deleting a board here and then the URL. So as I mentioned, you can explicitly define the URL you want to watch for. You can use regular expression. And what I feel is the easiest of them all is to use mini-match. So mini-match is this sort of wild card syntax. So I'll go URL API slash lists slash and then an asterisks represents whatever it comes after slash lists. So when I save that, it's actually matching the, URL, I just forgot to give it an alias. So let's do S delete list. And then after I click on the delete button, I want to wait for that delete list to happen. So let's save and voila, I have matched request, it actually happens. So we're all good.
19. Loader Types and Assertion Differences
Marcus raises a question about the loading of cards and data. He points out that there are different loaders and discusses the importance of distinguishing between assertions for visibility and existence of elements.
Marcus is saying, isn't the text loading cards and not loading data, it was actually the one, this one that I wanted to catch. There is another, there's also loading cards. So this is another loader, but I wanted to catch this one. And for some reason it didn't catch it. It was weird. Like maybe it wasn't appearing for long enough to be caught by the get command but yeah, I don't know why. Why did it happen? Like many application have these loaders, they sometimes take a long period of time to load, they are something like covering everything. So you cannot really start interacting with your app until the loader disappears or stops existing. And it actually brought me to one of the good points. Like when you have assertion, not be visible and not exist. Those are actually quite different from one another. So like not be visible is the element actually is in the DOM, but it's just hidden. It's you cannot see it but not exist means that there's no such element as this one. So make sure you differentiate with that, between those assertions.