In this workshop, I will introduce you to the migrator.cypress.io project in an unconventional way, where in addition to going over the Cypress commands equivalent to Protractor (and showing you how Cypress is simpler), I will also introduce you how I tested such a project evolving the automated test scripts on-demand, architecting the test suite in an evolutionary way.
Web Testing Architecture and Refactoring With Cypress
AI Generated Video Summary
The Workshop on Migrator.cyprus.io covered the migration of Protractor test scripts to Cypress using the Migrator.cyprus.io application. The workshop included running tests, discussing project architecture, and code improvements. Custom commands were introduced to reduce code duplication. The workshop also covered guidelines for writing code, version updates, and optimization techniques. Participants shared their experiences and recommendations for using Cypress for UI and API testing.
1. Introduction to Migrator.cyprus.io
Welcome to the workshop. My name is Valmir. I will introduce you to Migrator.cyprus.io, an application created by the Cyprus team for educational purposes. It allows you to understand how a test script created with Protractor would look like if you migrated to Cyprus. In this workshop, I will present how I tested this application using Cyprus itself. I will show you how to start a suite of tasks and evolve it on demand, adding and removing complexity as needed. There are 19 participants in the workshop.
Welcome to the workshop. My name is Valmir. I am a Cyprus ambassador. Can you see my... Besides my screen, can you see myself as well? Because now that he is sharing my screen, I can't see myself anymore. So, please let me know if you can see me.
So I was explaining before that in this workshop, I will introduce you to Migrator.cyprus.io, which is this application here. And basically, Migrator.cyprus.io is an application created by the Cyprus team as with educational purposes. And the idea is that it allows you to understand how a test script that was created with Protractor would look like if you migrated to Cyprus. So you can check it out at Migrator.cyprus.io. There is an example when you start the application for the first time when you click migrate, you see the equivalent code in Cyprus here. So in here you have Protractor syntax. And in here you have the exact same thing in the Cyprus site. Right?
And what I was saying is that for this workshop, I will present you how I tested this application using Cyprus itself. And in the way I will present it to you, it's going to be in an unconventional way. It's going to be maybe in a way that you might not be expecting. I hope you like the way I'm going to present it. I have already done this workshop in Portuguese, I'm originally from Brazil, so I speak Portuguese, and I have a YouTube channel. And in there I have done this same workshop in the way that I am going to do for you here today. And people pretty much liked it, so I hope you like it too. And what I'm going to show you is how you start a suite of tasks and you evolve it on demand. You start adding complexity just when it's needed, when the application is communicating to you that it needs more complexity, and also when it needs you to remove the complexity. So there's someone else joining. Let me admit this new person. So we have, at the moment, 19 people, so welcome, you all.
2. Introduction to Cypress Migrator Project
The project is available on GitHub. You can clone it, fork it, and run it locally. There are prerequisites and installation instructions provided. The project includes a test suite written in Protractor syntax, which can be migrated to Cypress. The Cypress commands are shorter and more concise. The project uses Cypress 11.0.1 and a custom command called 'press'. The cypress.config.js file is configured with the base URL. There are separate directories for support, end-to-end tests, and fixtures. The workshop is being recorded, and access will be provided for latecomers. Questions can be asked during the workshop.
The project itself is available on GitHub, on my profile, so I'm going to share it with you both here in the chat on Zoom and also on Discord in case you are there. So you can come to this project on GitHub if you want. If you want, you can leave a start. You can clone the project on your own computer if you wanted. If you want, you can also fork it in case you want to contribute back with anything else that might come to your mind. In this project, I have created some documentation, like very simple documentation for you to get started. So in case, you come here to GitHub.com slash WLSF82 slash cypress dash monitor dash task. You'll be able to clone it. After cloning it, I left some prerequirements here. So these are the versions of node and MPM that I used when I created this project. So if you want to use it, I recommend that you use these or later LTS versions. After you cloned it, you can change the director to go inside the project that you just cloned, and then you can run MPM install. After you install the dependencies, you can then run MPM test to run the test in headless mode, which is the mode where you don't see the browser being opened. But if you want, you can also run the tests with MPM run side, oh here, and PM run side column open to open the Cypress app where you'd be able to see the tests running in Cypress. in what is called interactive mode. The project itself is available on GitHub. So here is the application that we are going to test and here's the project of these applications. So if you want to come here, leave a status, clone the project for whatever you want. So this project is open source, the Cypress migrator, you can clone it and you can set up it locally if you want to have the project run locally instead of using the version that is running on the Internet, okay? And in case you want to run the same test against your local environment, I have created an npm script as well for you, which is npm run test local, or you can open the Cypress app to run them in interactive mode against your local environment, as well with npm run site column, open column local. After you run the tasks in headless mode, this is the summary of the results in my computer here. At the time I created this documentation, it took 51 seconds to run 31 tasks, which is what we have. If you want to know more about me, there is the link to my website here. And in my website, you'll find a little bit about myself. It's kind of a timeline where you can find links to my courses on Udemy. I have courses on Udemy about Cypress and about other technologies I have both in English and in Portuguese as well. You can find my profile on GitHub, my YouTube channel, most of the content on my YouTube channel are in Portuguese since I am originally from Brazil, and Portuguese is my mother language. But there is one playlist called English Content where I interview other Cypress ambassadors and people from the Cypress team. I even interviewed Gleb Bahmutov, who was the ex-VP of Engineering at Cypress. You can also find my Twitter profile on LinkedIn, Medium, and Def Community. Feel free to, like, from this project here, which I shared both on Discord and also here in the chat. You have access to the Cypress Migrator app. The link is here in the readme. To the project on GitHub, as well, and to my website in the footer here. All right? To get started, what I want to show you is, like, what does it do, right? So, for instance, this Cypress Migrator project here, it basically allows you to... And let me restart. The focus of today's workshop is not going to be on migrating commands from Protractor to Cypress. It's going to be on what I call evolutionary architecture for your task suite. Okay? And the migrator is just the application that will be tested. But it's important to understand what the application does, so I'm going to give you just a sneak peek of that. So, when you access it for the first time, as I showed you before, it has a task suite here written in Protractor syntax. So, it describes Cypress docs, then it has a test case. It should show the correct title, correct site title, and redirect URL. Then, there's the command that visits the page with browser.driver.get, passes the URL as an argument of this get command, then runs the assertions, expect browser.get title to equal ycypress, pipe Cypress documentation, and also expect browser.get current URL to equal to the URL docs.cypress.io slash guides slash overview slash ycypress. When you click migrate, you'll see the equivalent here in Cypress where you see that instead of browser.driver.get, we have a cy.visit. Instead of expect browser.get title to equal, we have cy.title should equal and the text that we want it to be equal. So here, should, in this case, is receiving two arguments, equal as the first one and the second one what we want it to be equal. And then instead of expect browser.get URL, a current URL to equal to the URL, we have cy.location. Get the href, and it should equal to this URL here. All right? So we can, for instance, type something else here, in Protractor. We would have something like element, and then we could pass, for instance, a tag A. If I migrate this to Cypress, it would be simply a cy.get or an anchor tag, right? So one thing that you will notice that I find nice is that Cypress syntax is much shorter than Protractor syntax. So you notice that in most of the cases, the command that you have in the right is shorter than the command in the left, meaning that the Cypress commands are shorter than the Protractor commands. If you have arrived late for the workshop, don't worry, the workshop is being recorded and you will have access to the recording afterwards. So this is the application that we're going to test, right? And, actually, I have already created the whole test suite in this URL that I have already shared with you, both on Zoom and also on Discord. And so we'll see here that it has in the package.json scripts to run the tests in interactive mode and interactive mode locally, where I am overwriting the base URL to the base URL of the local environment in case you're running locally. We will be running against a version on internet, and we have also scripts to run the tests headless, both against the production environment and also the local host environment. And finally, this project in the final version, which is the version that I'm showing you at the moment, it's using Cypress 11.0.1, which was recently launched, and it's using also this lib that I have created, which is called Cy-press, which adds one silly custom command, which is the press command. So you can change, for instance, you can do Cy.get, get an input of type Text, type my name, for instance, Valmir,.press, Enter, for instance, this is like it adds this.press command, basically. Other than that, we have the cypress.config.js file, where the only thing I'm configuring here is the base URL to https://migrator.cypress.io, this application here. This is why when I'm running locally, I overwrite the configuration. All right? What else? On the Cypress director, we have three different directors. We have support, where in here and basically importing my library that is defined in the package.json. That when I install the dependencies with npm install, it sends you the node modules directory. For some reason, there were uncouched exceptions in this application, so I have to add this line here as well. And then I'm not going to look into the end-to-end and fixtures for now, because you'll see it in a moment. But basically, here we have one test, migrator.cy.js, and in the fixtures we have test scenarios.js. But I said to you that I'm going to present this project to you in an unconventional way. Let me see, it seems that there's more people waiting to join, so let me unmute them as well. So for you that has arrived late, don't worry, the workshop is being recorded and you have access to it afterwards, so you can watch what you have missed because we have already started for a few minutes ago. And if you have any questions, you don't need to wait until the end of the workshop.
3. Running Tests and Migration Process
In this part, I will run the tests on the Cypress app and show you the migration process. The tests access the migrator.cypress.io page, where Protractor commands are typed on the left side and the migration results are checked on the right side. The tests also perform water checks. After the tests finish, we can use the time travel functionality of Cypress to see the application's state at different points. The tests cover various migrations, such as submit commands and send keys. Cypress provides a productive and easy-to-read API for testing in the browser.
You can ask questions beforehand. I'll be, while showing you things here, I'm going to be looking both through the chat here on Zoom and also on Discord, in case you prefer to use Discord instead of Zoom for chatting. So I'll be in both here. I just put them both in my other screen now so I can watch to both of them at the same time and I don't need to be switching desktops.
Let me see. There's someone else waiting, so let me admit. For you that joined right now, the workshop is being recorded and you have access to the recording afterwards.
So, okay, I was saying I'm going to present you this project where I tested Demigrator in an unconventional way. And what I mean is that I'm going to present you the evolution of these tests. But actually, let's run the tests first. I think it's going to be nice if you see what's going on before you start looking into the code.
So I have Cypress, the app, open here in my screen, and I'm going to run the tests on Electrum, just because then I don't get confused with another Chrome open. I'm going to click in the migrator.cypress.cy.js, which will basically run the tests in what is called interactive mode. So what the tests do is they access the page, migrator.cypress.io, where we have in the left side, where we can type the Protractor commands, and then I click the Migrate button, and I check that the correct thing was migrated in the right side. And I also do some water checks that you'll see in a while.
So we have 31 tests. So we are almost there. I think it's important that you see the tests running before we go and look into the code. So as soon as it finishes, we can use time travel functionality of Cypress. By the way, if you are not very familiar with Cypress, Cypress is a test framework that allows you to test anything that runs in the browser. And it gives you a very good development experience. It allows you to write tests in a very productive way, and it gives you all the tools that you need to be productive and to debug your tests when they are failing. Different than other tools like Selenium-based, for instance, where you would run your tests. And even if you are running them in headed mode, where you can see the browser, right. When the tests finish, what happens usually with this old tools is that the browser closes and you don't have any more evidence other than a stack trace on your command line. So one of the nice things on Cypress is that, after the tests are finished, the browser is kept open and you can see how the application is when the test ended. But not only that as you can also navigate back to each of the tasks.
So for instance in this one here, we are migrating Protractor's submit command into Cypress submit command. And then we can use the time travel functionality to see how was the application when I executed the visit command to slash, which is basically the homepage. And this is how the application was rendered at that time. Then we can see that I got some text areas, two of them were returned and I gave to them an alias called text areas. Then I got the first one and I pressed select all. Then we can see the before and after where we can see here that it selected the content. Then I typed clear and then it was all clear. So I start in a fresh state with everything clear and then I can finally get the left side. And then I give to this first text area an alias called left side editor. And then I get the left side editor and I type something. Here you can see the before and after. So this is how it look before and after I typed. I typed elements by CSS form dot submit. And then I find the button that contains the text migrate to Cypress. I click on it before and after, and then afterwards, after I clicked, I can see here that it transformed this command here into a Cypress command. And I can also see that it pointed me here to the commands that are being used here. So in here I'm using site of gap and dot submit. So it shows me links to the official Cypress documentation, both for the Cypress.gap and for the dot submit command. And these are things that we are testing. As you can see, when I clicked the button, well now it kind of got lost, but when I clicked the button, the viewport was in the button itself, so I had to scroll back, I had to go to the text areas, get the last one, which is now the one on the right side, scroll it into the view to run my assertions that it expected to see a site.getform.submit and to, no, it expected this text here, site.getform.submit to include site.getform.submit. Basically I'm asserting that it's showing what it should be showing. And then afterwards I come to this section here where we have the Cypress commands that were used and I iterate through them. First I just check that it contains site.get. Then I find the first, I find the anchors, it returned two elements. I find the one with the index zero and I check that it has the correct href attribute which is on.cypress.io.get pointing to the correct documentation. Then I do the same for the second one where I get them all. I check that there is the submit here. I find both a's. Now I go to the one with the index of one which is the site.submit and I run another expectation that it has the correct URL, the correct href value for that specific anchor tag. In all the tests they do exactly the same thing. They visit the page. So for instance, in this here, we are seeing that we can migrate from dotsubmit to dotsubmit. In this case it's just the same, but instead of element by CSS, we get site.get. In other ones, for instance, here, we are migrating the send keys into dot type. So if we go here for instance, let's see this one here, we can see that I typed element by CSS, my selector.sendkeys, ABC, and what I have in the right side of getSelector.type, ABC, which I find dot type a lot more clear than send keys. This is something that I find really nice on Cypress. Like, its API is very easy to read and easy to follow.
Okay. So this is what the test does. All the tests, they visit the page, clean the text area in the left side, type a protractor command, click the migrate, and check that the migrated snippet in the right is correct, and that here below we have both the commands that were used here in the right side, so that's Cy.get and.type. So here we have Cy.get and Cy.type. And we not only check that these values are here, but also that they are pointing to the correct URL in the documentation. So if we go, for instance, here we migrated element.byCSS to Cy.get.
4. Introduction to Project Architecture
We'll see here below that we have Cy.get. And if we click on it, we are directed to the.get command in this official Cypress documentation. For this application, we have 42 commits, and I'm going to start with some of the first commits. The initial commit was when I created a project on GitHub and it basically created the license file. I will present the architecture for this task in an unconventional way by showing you the history of commits while I was creating the project.
We'll see here below that we have Cy.get. And if we click on it, we are directed to the.get command in this official Cypress documentation. Okay. Now to the fun part, hopefully. So for this application, we have at the moment 42 commits, and I'm going to start with some of the first commits. So the initial commit was when I created a project on GitHub and it basically created for me the license file. So that's basically it. When I said that I would present you the architecture for this task in an unconventional way, it means that I'm going to show you through the history of commits while I was creating the project.
5. Creating Cypress Project and Writing First Test
I created a Cypress project with various commits. The first commit created the necessary files and installed Cypress. I configured the project, specifying that I would not use fixtures initially. I created a test suite called Cypress migrator and wrote the first test, which migrates Protractor's browser.get command. I cleared the text areas and typed in my personal website. Then I used cy.contains to find and click the 'migrate to Cypress' button.
Again, if you have any questions during the workshop, feel free to shoot them both on Discord or here in the Zoom chat. And let me see here. So then I have a commit that creates a Cypress project where I'm basically creating the Gitignore file, the README file, a Cypress example file, cypress.env.example file, the package log and the package.json where I have already installed Cypress, where in that moment, I was using version 10.4. I actually migrated to 11 today, I think, and I've been upgrading to the latest versions as they come.
Okay, so this is a not very important commit. And for the important commits, I put in the commit message a V and a number. So V1, V2, V3, which is like the versions, let's say, of this test scripts while I was creating them. And the first one is, the V1, is lots of duplications. So this is the name of the commit. I have created some tests with lots of duplicate duplications, and here I have then configured Cypress. When you install Cypress and you open it for the first time, it will bootstrap itself. It will create the Cypress.config file. It will create the Cypress directory with the ensudev. You can even tell it to create some tests for you. It will create the support directory with the ensuev.js file. And then I started configuring my project, where I told Cypress that, in the first place, I would not use fixtures. You saw that in the final version, I am using fixtures, but when I started, I said, you know what, I don't think I'm going to use fixtures, so I'm going to configure Cypress saying fixture folder false. I'm not going to use it, for the moment. And then I created this migrator.cy.js file, where, in here, I have created a test suite called Cypress migrator, so when you create a test suite, you call the describe function, which receives two arguments. The first one is the title of your test suite, and the second argument is a callback function. Inside the callback function is where you, inside the body of the callback function is where you add your tests, and your tests follow the same syntax, but instead of describe, they are if blocks. The first argument of your if block is the title of your test, and the second argument is a callback function. And inside the body of this callback function is where you have the details, the implementation details for that specific test. So the first test is, it migrates Protractor's browser dot get. So I run inside of visit to my greater dot cypress dot io. Then I do a site up yet to the text areas that are inside an element that has both the classes side by side and dot vs. So let me actually do one thing here. So you can understand what's going on. This is a text area, and this as well. But this one in the line in the right is read only, and this one is not. Because this is where we type things to migrate to the other side. Let me see, it seems that there's someone else. Let's unmute this one person. So if I inspect, what's going on? Let's inspect. And let's inspect this one here. We have editor wrapper, and we have a section, and we have a div, and we have another div. Let's see, where is it? Okay. Then we have these two divs here, right? And we have another div. Let's see. How is it? Another one. Another one. Oh, my gosh, it's so much stuff here. Well, it's going to be difficult to find, it seems. But somewhere there is a text area. Let me see if I can maybe try to find the text area from the bottom up. It's quite difficult to find it. Well, anyway, what happens is inside there, there is the text area. This class side by side is because both these elements are, as you can see side by side, right? There is one in the left side and one in the right side. And both of them also have the dot, the, the vs class. So dot in front is just to say that this is a class. So I'm basically using site.gap to get all the text areas that are inside an element we have, which has both the side by side and.vs and the vs classes. Then I, well, I was here actually, I'm sorry. So I get the text areas and I say go to the first one and clear because you remember that when I accessed it for the first time, it already has some text there. So I want to clear it. And then there is something weird, which I, which is I'm clear clearing it again. And that's fine. It's the first version. I'm not going to worry about it. The thing is that when I was testing it, sometimes I would type, would clear the thing and it would still miss some lines like it would clear it like this. So I would clear it again just in case to be sure that everything was correct. Is this the right way to do it? Surely not. But as I said, I'm OK with that because this is just the first version. Ideally, what we should be doing is wait for the application to be in the state we want it to be so we can start interacting with it. Since it was the first version, I just left it this way, clear it twice with the second clear, I'm pretty sure that everything was cleared. And then I typed browser.get and my personal website premiere.dev. Then I use side up contains to get a button that contains the text, migrate to Cypress and I click on it. So I'm coming here, clearing this thing, typing something, finding the button, clicking the button. Right. And then I get again the text areas. But now instead of the first, I'm getting the last, which means the one in the right side.
6. Migration of Commands and Suggestions
I scroll the button into view and change the dot should attribute. The index zero of the last text area should include site dot visit. I migrate other commands like element by CSS and element by class name. I use site dot get with the syntax for class and site dot contains for CSS containing text. The Cypress team adopted my suggestion to use site dot contains with two arguments instead of site dot get dot contains.
And then I scroll it into the view because when I click the button, there was a scroll to the button itself. I had to scroll into the view to go back, to have the text area back in my view so I could run some assertions on it. And then I change a dot should where the dot should basically gets as attribute. The dot should receives as in this case, it's receiving as an argument a function, an arrow function. And this arrow function is receiving as an argument the text area itself. The one in the right, which is the last. And then I expect that the index zero of these text, the value of the index zero of this text area includes site dot visit. HTTPS column slash slash on your that. So I type browser dot get and I got site of visit instead. OK, and then I did the same thing for other commands. So now I'm migrating protector's element by CSS. I visit, I clear twice. I type, I click and they migrate. I go to the last one to the last text area. I scroll into the view and now I check that element by CSS. Selector is the equivalent in the Cypress world would be side of the selector. And then I do the same for protectors element by class name, where in here protractor has a specific command to get elements by their class name, which is adamant by class name sample class, for instance. And in the Cypress world, it's just another site. Got with the syntax for class, which is adopted in front of the value. Then protractor also have a command called CSS containing text. So I can identify an element by its CSS and the text content, right? So here I type adamant by CSS containing text selector and the text that I want this element to have. I click the button and I check that it is transforming to site out GetSelector dot contains sample content. This is how it was when I tested the application for a first time. You see that if we copy this is specific thing here and we type it here and we tell migrate. Nowadays, it's just a site out contains with two arguments. The selector as the first one and the text as the second. This was a suggestion of mine. When I was experimenting, I felt it weird that it would migrate to site dot get dot contains when it could use just site out contains passing two arguments. So, I suggested that and the Cyprus team decided that that was a good idea. So, nowadays, this is not what this migrates to anymore. Instead, it migrates to just site out contains with the selector and the text that we wanted to contain.
7. Code Improvements and Custom Commands
I asked for feedback from the audience and made some improvements to the code. In version two, I moved duplicated code to a before each hook to eliminate duplication. I used aliases to avoid duplicating selectors. In version three, I created a custom command for the migrate action, but later realized it was not necessary. The tests became shorter and more concise. Overall, the code was improved and duplication was reduced.
I will pause for a moment and I will ask you for some feedback. How are you feeling? Are you enjoying it so far? Is there any question, anything that you want me to clarify before I move on? Feel free to shoot your questions or feedback both in the chat here on Zoom or on Discord as well in the workshop under the TaskJS workshops channel.
Okay. So I already got some feedback here that it's very interesting and it's nice. So thanks to both of you that gave me this feedback. If I can make it nice for one person, I'm already happy. If I can make it nice for all of you, even better. So someone else here said, that's good as well.
All right. So this is the first version. And the commit was… has lots of duplications. And as you can see, I am duplicating a lot here. Like, I have one, two, three, four, five tasks, and they are basically doing all the same, visiting the page, typing the command that I want to migrate, clicking the button and checking that it was migrated correctly. So there is a lot of duplication. So let's see how version two is.
On version two, I said move duplicated code to before each hook. So Cypress uses Mocha behind the scenes. And Mocha is a test library for unit testing. And it gives you some very helpful commands like before each, where you can remove some duplicated code for things that every task should start in that same state. So, as you might have imagined, every task needs to visit the page, clear the field before it starts. So, I moved all that duplicated code to a before each rule. So, now, right after the describe, instead of having the test, we first have a before each function, which receives, as an argument, an arrow function. It could be a normal function. It doesn't necessarily need to be an arrow function. I like arrow functions because they are shorter. And then I put here the site of visit, the get of the text areas, where now I am not only getting the text areas, but I'm also using the dot S and passing a string text areas to give an alias to this thing here. So, I don't have to duplicate this selector anymore because now I have an alias. This is something that not many people that use Cypress know that you can give alias to elements. So, you don't need to duplicate the selectors. And then people start putting selectors in variables or in other files and things like that because you don't want to duplicate the selectors. You don't necessarily need to do that if you know how to use Cypress API and, for instance, give alias to elements. You don't need to duplicate this anymore because now you have an alias here, which I'm using right here. Then I get the first text area. I clear it twice. It's okay for now. Then I... Then I give it to the first one. I give an alias of left side editor, so I gave an alias to both the text areas and also to the one in the left. Then now that I have an alias for the text areas, for both of them, I can use the alias again here instead of duplicating this selector here, which is a little bit more complex than just add text areas, and I get the last one, which is the one in the right. And since it's the one in the right, I give to it an alias of right side editor because I'm going to use it afterwards. And then the tests start, where now I don't need to do all these duplications in the test. In the test, I am migrating Protractors browser.get. So I basically get the left side editor, and I type what I want, I click the migrator, the migrate button. And I get the right side editor, I scroll it into the view, and I run my assertion. As you can see here, I have removed lots of lines and I added just one line. Here, I have removed two and I added just one. And here it could be one and one because I just broke the line and it could be all in the same line. But as you can see, the code is shorter. This commit, I deleted 32 lines, and I had it only 19. So I made my test suite a little bit shorter and I don't have that much duplications anymore. But this is just version 2, okay? So on version 3, I decided that I would create a custom command to the migrate action. Looking into it today, I don't think I would do it because I basically am creating... Instead of doing siloed contains button that contains the text, migrate to Cypress and clicking on it, I created a custom command called migrate, side out migrate, which does this thing for me. But as I was saying before, this dot click could, without any problem, be just on line 19. And I would still just have one line. It would be pretty clear what it does. It gets a button that contains that text and clicks on it. But for the sake of doing this on demand refactoring that I mentioned earlier, I decided that I would have some custom commands. So I created the migrate custom command. And now instead of doing this here, I just say side out migrate, side out migrate for all the tests. And in the test file itself is where I created a custom command. I was just playing around, experimenting with things. I didn't know if this would be the way to go. So instead of putting the custom commands in my support file, I just put them directly in here and I use them directly because they are not used in other files. There's no other test file anyway. So I put them directly in here, and this is the definition of the custom command. So I am using the Cypress module from the commands module from the add function. Where I'm adding a migrate command and the migrate command executes this function here, which basically runs Cypress contains button, migrate to Cypress dot click. And the test is a little bit shorter. Every test is one line shorter.
8. Custom Commands and Code Reduction
In version 3, I created a custom command for migrating and decided to create one for the assertion as well. Although I prefer to have assertions in the test file, this case warranted a custom command. I replaced the assertion code in all tests with the custom command, passing the desired code snippet. The custom command definition is in the test file, using Cypress.commands.add. This reduced the code by 27 lines and added 13. The test suite is now shorter and more concise. I also removed unnecessary aliases.
As I said, it could be just the same if the click was in the same line, but in the end I have deleted 10 lines and I added 10 lines because although here I removed 2 and added 1, in here I had to add the custom command, which added a few more lines. So it's even, okay? And this is version 3. Since I created a custom command for migrating, I decided why not to create also for the assertion. I'm not a big fan of creating custom commands for assertions. I do prefer to have the assertions in the test file with the details that they should have. But in this specific case, the assertion was a bit too complex. And then I decided that it would be maybe worth to extract it to a custom command. So the name of the... So I basically replace this in all the tests, as you can see, to side.assertRightSideEditor code snippet, and I pass to it the code snippet that I wanted to have in the right side, okay? And I put the definition of the custom command directly in the test file as I have done with the migrate command. So I have like Cypress.commands.add, the name of my custom command, and the function that it executes, where in this case, it receives as argument a snippet, and it does the side.get, so the right-side editor is curl it into view and chain it should, that receives a function that receives as argument the text area itself and expect the value of the text area to include the snippet that we are passing to it as an argument in here. Okay, this one removed 27 lines of code and added only 13. So my test suite is getting shorter more to the point. Okay, then I have noticed that I was using some alias that was not necessary anymore.
9. Commit Description and Abstractions
For V5, I decided not to have just a title for the commit, but also a description. This is valuable when reviewing code from others. The unnecessary element alias was removed. Abstractions should only be added when needed. Giving context in commit descriptions can help with code reviews.
So notice that until before, we have just the title of the commit. For V, until before, we had just the title of the commit. For V5, I decided not to have just a title for the commit, but also a description. And this is something that I find very valuable, especially when I'm reviewing code from others, that if the title is not specific enough, I can add more information in the description of my commits. So the title of my commit is removed. Unnecessary element alias. And then I gave some more context. Instead, call it directly where it's used, now that it's used only once.
So let's see the code. And in here, if we expand this, in my before each hook, I was giving an alias to both the text areas. I was giving an alias to the first one as left side editor. And I was getting the text areas dot last and giving it an alias to the one to the last, which is the one in the right side editor. But after I have done this refactoring and I removed all the duplication and I moved things to the before each, I wasn't actually using this alias more than once. So I like to add these kind of abstractions when I'm using at least this thing three times or at minimum two, but I prefer when it's like I repeated this three times, then it's time to move it to an alias, for instance. But after I did this refactoring, I was using it only once. And so I felt that it was more complexity to add this alias here, then just do something like that, side out. Get the text errors dot last, which is the one in the right side. So this is a very short commit. And the point here is that we don't need to add abstractions when there's no need, because the abstraction that we created is just been used in one specific place. So there's no real need for the abstraction, which in this case would be just this alias here. And I also mention that you can always give more context to help people review your code. Not only giving a title, but also a description for the commit itself.
10. Version 5: Custom Command for Typing
Here we are on version five. So let's go to the newer commits, where we can move on to version six, where I added a type on left side custom commit. Again, if I was to do it today, I would probably not create this custom command, but I already had a custom command for clicking the migrate. I had a custom command for the assertion, so why not create a custom command for the action itself? So action for typing in the right side editor. Oh, that's nice. We have already 28 participants. I hope you are enjoying the workshop. If you have just joined, don't worry. The workshop is being recorded and it will be available to you all in a few days.
Okay, so I was saying here, now, instead of doing psy.get, left side editor,.type, browser.get or.type, element-by-CSS or element-by-class-name, now I have a custom command psy.type on left side editor and here is the text that I want to type into the left side editor. And then I have the definition of my custom command right below here, just above the migrate command. So cypress.commands.add, the name of my command, which is exactly the name you see here, type on left side editor and the function that will be executed, which receives the snippet. It gets the left side editor by its alias and then types on it the snippet that was received here, which, for this task here, the snippet was elementbycssselector.sendkeys, for this one was elementbycsscontainingtext, selector blah, blah, blah. And now we can see that the test file itself, the tests themselves, they were pretty straightforward. Like, in the before each, we have all the parts that is not being duplicated anymore, and the tests, if we look here, it starts on line 12 and it ends on line 18, the first task, it has one, two, three lines. It types on the left side, it migrates, which is basically clicking the button, and it runs the assertion. Same for the second one, types, migrate, assert, type, migrate, assert. So I am making my test suite shorter and more to the point. Okay? Then, we have version 7, where now I decided that it was time to move the custom commands to the right place. So, if you don't know, Cypress when it starts, it when it bootstraps itself, it creates a support directory where you can add your custom commands. So Cypress has its own commands, like site.visit, site.get, dot type, dot click, site.request, site.intercept, lots of commands, you can look through all of them at docs.cypress.io. But you can create your own, as I was doing until now, and I was putting the definition of this custom commands in the test file itself. But Cypress has this support directory where you can put this custom command there, and they will still be available for you in the global site object. So in this commit of version seven, I basically cut all these custom commands that were defined in a Migrator.cy.js file, and I put them into the commands file, which is inside of the support folder inside of the Cypress folder. So I basically put them there, cut from one place, pasted in the other, and then in the end to end.js file inside of the support file, I'm importing the commands basically. So the commands are available in my test, and then my test file now has only 50 lines of code, and it's pretty straightforward as you can see here now. Each task has only three lines in sight. If we exclude the empty lines, that, I prefer to leave just for giving it some breath and you can read it easier. But the tasks, they are pretty straightforward. And when they end, it's done. We have five test cases for this test suite, and they do basically the same. They type, they migrate, and they assert. Okay? But they type different things and so they assert different things. And the B4H has all the steps that all the tasks need, so we don't need to repeat them all around. So this is version 7.
And then we have version 8, where I set shortened spec file. And here we can see that I also have a little bit more details in the description of the commit, so by iterating over an array of test scenarios based on a fixture file. This commit here is one of the most important of this project because this is the commit that made this test suite to become what it is today. Okay? So pay attention to this one. Remember that when I configured Cypress in the first place, I said, you know what? I don't think I'm going to use fixtures. So I configured Cypress with fixtures folder files. Now I have removed, in red here, we have the lines that were removed and in green, the lines that were added. So I have removed this here because I decided, you know what, now I think I'm going to use fixtures and I'm going to use fixtures to shorten the spec file, as my commit message says. In my test file...
11. Migration Process and Code Improvements
In version 8, I stopped using custom commands and moved the implementation details to the test file. This reduced duplication and made the code more concise. In version 10, I added an extra assertion to test the API details element. I also added Cypress commands to the fixture objects for each test scenario. Version 11 removed duplication from the fixture file by using template literals. The changes made the code shorter and eliminated repetitive phrases.
Second one, migrate pro-tracker element by CSS. In the first one, the snippet to be migrated will be browser.get while in the second, it will be elements by CSS. The migrated snippet in this case will be site.visit while in the second is site.get and so on and so forth. But the idea is now that I have done that, I can add as many scenarios as I want and I don't have to change one line of code in the test file. I just add another. I just add a comma, another object with title snippet to be migrated and migrated snippet. And when I run my tests, I have one more test case. And if I add more, I will have more test cases. And that's as easy as it would be. So before, I think, our test file had around 50 lines. Now it only has 21 lines. And it's very true to the point. It's basically this thing here, right? We are iterating over each test scenario. We are creating EAT blocks for each of them. And for all of them, we are doing the same thing, but we are passing different arguments, OK?
Now we are in version 8. So let's go to version 9. There are 42 commits. Not all of them have versions. I'm going to go until version 21, which is the last one that has significant change. And then we can briefly look into what I added later, but it's basically upgrading Cypress and doing some other things. Maybe we can look into the GitHub Action workflow that I have created as well. So I was, until now, until version 8, I was using custom commands. And then on version 9, I decided not to use them anymore, and there is a description for that. And I said, do not use custom commands since they were used only once. They are not needed anymore. So, again, I said that before for the alias. If you are creating an alias, you are using it only once, there is no need for an alias. The same applies for a custom command. If you are creating custom commands, you are adding another layer of abstraction to your task suite. You're moving these commands to the support file. And then you notice that, you know what? Now that they have migrated everything, I'm using it only once because now there is only one block which is executed based on how many fixtures, how many test scenarios I have in my fixture file, right? So instead of having these abstractions of type on left side editor site.migrate and site.assert, I prefer to have the implementation details in the test file itself. Because if I want to understand what's going on, I don't need to go to another file. Everything is in here and there's no duplications, right? I'm not duplicating anything. I created a smart way to create many test cases using the exact same structure based on fixtures, okay? So I basically removed the commands completely. I removed the import from the support end-to-end file and inside I have the get left side editor and type the snippet to migrate, to be implementation details. To migrate, to be migrated, I get the button that contains the migrate to Cyprus and I click on it and I get all the text areas, go to the last one which is the one in the right side, this one here, and I checked that it has the correct value, that the migrated snippet is there, all right? Let's go to version 10. Let me see here if we have any questions. There is some mention from Duke. Nice design with fixtures directory. Easy to add tests later. And you'll see that in a few commits, I'm just basically adding more tasks and it's as easy as adding more entries to that file. So on version 10, I updated tests with an extra assertion. So, so far I was just testing that. If I type something here and I click I'll get something else here. But I wasn't testing this part here below. So this is what I started testing on version 10. So I decided that besides the title, the snippet to be migrated and the migrated snippet, I would also add a Cypress command to my fixture object, to each of my fixtures in the array. And then besides running this assertion here that the last text area would have the content that it should have, I'm also getting the API details element and I am asserting that it should contain the Cypress command, which in the features basically besides the snippet to migrate, I added Cypress command, Cydot visit, Cypress command, Cydot get, Cypress command, Cydot get, Cypress command, Cydot contains, Cypress command, dot type, because when I migrate, I see here a list of the commands. And in here, for instance, there is just one command, but if I was, for instance, doing a dot click on it, click, and I migrate, I would have actually here Cydot contains, select, sample content dot click, and in here, I would have actually two commands. In version 10, I'm just checking the first one. So I'm not passing more than one. I'm just checking that it has the one that I think is the most important. Afterwards, you see that there is a commit that introduced a way to check them all. So that's version 10. Let's see. Then on version... So this is no version, so I'm going to skip it. Remove duplication from fixture file. As you could see in the fixture file was saying migrate protractors and then the command. So it was migrate protractor... It was migrate-protractors-browser.get migrate-protractors-admin-by-css. So the migrate protractors was always repeated. In this case, I decided, you know what? I could simply use template literals. So in here I passed just what changes, which is the command itself. Send keys. By css containing text, by plus name, but the other part doesn't change. And so in here for my it block I use template literals where I interpolate the string my grades, protractors and then the variable, which is the title that I am receiving. And then this way I am not reducing the number of lines. I'm just leaving my feature value property a little bit shorter and without duplications in that sense. So this is a pretty straightforward commit version 11.
12. Version 12: Improved Object Attribute Naming
In version 12, I improved the naming of object attributes by changing 'snippet to be migrated' to 'snippet to my grades'. This made the name shorter, more concise, and still clear in its meaning. I followed the principle of leaving the code cleaner and made the necessary changes in the code and fixtures accordingly.
Version 12, I decided to better name an object attributes. So I had snippet to be migrated and then I changed it to snippet to my grades. So just left it a little bit shorter, but still with a very meaningful name that you look into it and you understand what it is. It's the snippet to my grades, basically. So I like to follow the boys' culture of leaving the campground a little bit cleaner than you found it. So I was, you know, reviewing my own code and looking into it and I thought, you know what? This could be, instead of snippet to be migrated, just snippet to my grade would be shorter. It would be concise. It would still be very clear what it is. So I just changed that and then I changed it when I am destructuring all the info from the scenario, when I am iterating over my scenarios. And then I changed in the fixtures the name of the property as well accordingly. So this is version 12.
13. Migration Process and Code Improvements
In version 8, I improved the migration process by not using custom commands. This made the code easier to understand as all the implementation was in the test file. The inspiration for this approach came from Backstop JS, a tool for visual regression testing. I also made improvements in version 13 by clearing the text areas only when necessary, reducing duplication. In version 14, I allowed testing more than one Cypress command by modifying the fixture to receive an array of commands. The Cypress team added testability to their front-end application by using data test attributes, which I utilized for assertions and actions.
Okay, so let's see in which one we were. Okay, so we were in BetterName object attribute V12, so V13, clear twice only when needed. So I did some improvement. Remember that in the beginning we were getting the first element and then typing using the.clear command from Cypress and then clearing it again, which was super weird but it was needed because sometimes the application here wouldn't be in the correct state and when I would clear it would still leave something. So I would have to clear it again, okay. But then I said, you know what? This is still not ideal, but it's a little bit better I think. So I'm going to get the text areas. From the text areas I'm going to get the first, which is the one in the left side, which is the one that is not read only. Before clearing, I'm going to type between brackets, select all, to basically do something like this. I would be selecting all the text and then I would do the.clear to clear it all with that clear, okay. And then I would chain a.then, which can receive as an argument a function. This function can receive as an argument the subject element itself, which in this case is the first text area. So this is why I'm calling the variable text area. And then I'm saying, I'm adding a condition. If there is still some value for this specific text area, then log in the Cypress command logs that you are clearing again, wrap this text area, and then execute the.clear command again. But when I added this, select all and clear, it solved 98, 99% of the cases. In some cases, it was still leaving something there, and then it was reaching this if condition. It was returning true, and it was clearing again. There were two different solutions for the thing. One was selecting all before clearing, and then if there's still some value, which is a text area of index 0,.value,.length, if there is still some value there, then log that you are clearing again, and do clear it again. So it's better. It's not executing the.clear twice if not needed, only if it's really needed, okay? And I fixed the test title which is not relevant.
14, allow to test more than one Cypress command. So remember that I said that before I was only testing the first one here. So I want to test them all because if this command here is actually returning not just site.contains but also a.click, I want to test that both of them are displayed in here. And even inspect here to show you one thing. Let's see. More details. Is it what I'm using? API details. Yeah. As you can see here, this div has this DataTestAttribute, API details, and inside this div, we have an unordered list which has two list items. The first one for the side.contains element text and the second one for the side.blick. So the Cypress team, they added what I like to call testability in their front-end application adding data test attributes that I could use, to make assertions, in this case, or it could be to run actions as well. So basically here, let's see, allow to test more than one Cypress Learner. So before my fixture was receiving a Cypress command, now it's receiving Cypress commands The fixture itself now, instead of Cypress command it's receiving a string. It receives an array. And if there's just one command, it's going to be an array with only one string inside, which in this case is cy.visit. In this case, it's cy.get. But if there is more than one command, like this one that has cy.get and a cy.contains. Then I can put more. And if there were three, four, as many commands I needed, I could put them because now, cypress command is an array. It's not a string anymore. And then in the test file, after I run the assertion, after I run the assertion that the correct snippet was migrated correctly, I also iterate over the cypress commands. And then in the cypress commands, I run for each and I pass a callback function. This callback function receives each of the commands. So in this case, it will receive just cy.visit. But in this case, for the first iteration, it will receive cy.get, and for the second it will receive cy.contains. And then I check that the API details should contain the command. So for the first iteration it would check, does it have cy.get? Yeah, it does. Let's go to the second iteration. Does it have the cy.contains? Yeah, it does. So it would do it like, look if cy.contains is here and if cy.click is here.
14. Migration Process and Code Improvements
I added more assertions to my tests in version 14. In version 15, I added new tests by updating the fixture file. Cypress commands are shorter and easier to understand than Protractor commands. The Cypress project allows for easy addition of new tests by adding entries to the fixture file. The tests cover various scenarios, including element by ID and element by CSS. The Cypress commands are concise and provide a more optimized way of testing. The workshop participants share their thoughts on the approach, and the speaker recommends optimizing tests through HTTP requests. The speaker also suggests visiting their website and taking their Udemy courses for further learning. The chat includes discussions on file organization and the benefits of smaller files. The speaker mentions a tool called Better Code Hub for code quality analysis.
Okay, so now I added a little bit more assertion to my test. I'm not just checking that one command is there, but all of them that should be there are there. So this is version 14. Then, on version 15, I added a bunch of new tests by just updating the fixedroot file. That's the beauty of this project here. Now if I want to add lots of new tests, as I was saying to Olga first, like now you can see that it's pretty easy to add the new tasks. You can just add new entries to your fixture with different values for the same attributes, title or snippets, to migrate, migrated snippet, and Cypress command. So if I want to test how this element by ID works, this is how it looks in protractor syntax, and this is how it would be in Cypress. So instead of element by ID and then your ID in here, you would have site.gat, hash my ID, is displayed. Instead of have expect element by CSS, selector is displayed to be true, you would have site.gat, selectors should be visible. And in this case, since I would have site.gat and should, I would have in the Cypress commands, site.gat and.should, is present, expect element by CSS, the selector is present to be true. It would be transformed into Cypress. Get selector should exist. Again, as you can see what I said in the beginning, most, if not all the Cypress commands are shorter than the protractor ones. This one is shorter than this one. This one is shorter than this one. And the same goes on and on and on. Even browser.getTitle is just site.title, right? And they are still very easy to understand. Actually, in my opinion, a lot easier to understand than what we had with protractor. I'm not going to through all of the scenarios. It's just all the same, just passing different values for the title, for the snippet to migrate, for the migrated snippet, and for the commands. Somewhere we might have, here, for instance, I am migrating expect element by CSS, all the li's inside of the unordered list and getting the count of them and expecting that they should be three. In Cypress, it would be site.get ul, li, its length should deep equal 3. In this case, it's a little bit longer than the one above, but still very easy to understand, very easy to read. You get all the li's inside the unordered list, get its length, and it should be deep equal to 3. And then, because I have site.get, I have its, I have should, in the Cypress commands array, I can have site.get, its, and should in here. I don't have to change anything in the task code itself, just in the fixtures, and I have lots more, a lot of more tasks now.
Let me see here, we have a few more messages in the chat. There is one, I agree with Olga, the first way seemed easier to understand. Well, I hope by the end of the workshop, you can change your mind, and if you don't want, that's okay as well. I'm not here for you to change your mind. I'm just showing you the process of creating this task suite in the way that I found that would be optimal. Here it probably makes sense, since there's only null test files. I think it should be one test file. Yeah, but still, Rosislav, if there were other test files, the other test files would be testing other things, not the same things. So I still think that it still makes sense, because the other things would have other commands and they wouldn't need anything from this test file. Because our tests, they should be independent and we shouldn't be over-testing things, which is something that I see most people coming from other test frameworks doing, over-testing the application. What I mean by that is doing too much through the UI instead of creating more optimized ways to do the things. And Cypress allows you to do things in a more optimized way. If you want to learn more about how to optimize your tests, I would encourage you to come here to my website, from me.dev, and go to my courses on Udemy. And then if you go to the intermediate course, you see what I'm talking about. You can create all the preconditions of your tasks in a very optimized way, for instance, doing HTTP requests. And then when you go to your test, you reach the application with all the data that you need already there, and then you don't need to do as much through the UI, which, in the end, means that you wouldn't need to have all these abstractions and everything because you are doing the things through the UI only once anyway. Let's see what else we have here. Not the case with my app. I use lots of tests, CytoJS files, blah blah blah. Duke seems to be more of a style preference. In the past, my teammates stressed that things needed to be as dry as possible, but I find the testscenarios.js files to be very easy to understand. The objects are basically title, tab, task, expected result. Yes, exactly. Let's see, there's a message from Marcos. I think that this method is amazing. I'm not sure if people are aware that in the test runner, these appear as separate tasks so it is still easy to trace where the bug was found. I will definitely adopt this approach. Can Cybot Sessions be used in your before each? In this case here, I don't think it would really be needed because I'm not doing any kind of authentication or anything. We'll have to experiment, Marcos, to let you know if I could save in the session the state of the application with already everything cleared. That's a good idea, actually. I'll try it out afterwards. Just need to get used to it. Objects are okay. I use the same approach frequently. I just don't like lots and lots of small files. Why not, Olga? Small files, they are easier to read than big ones where you have to have lots of scrolling. I actually do prefer smaller files than big files. I actually recommend you to go to a website called bettercoderhub.com. This is a SaaS that I have used in the past, and they have actually, it's similar to SonarQube, but instead of... It clones your repo, let's say, and it looks into code smells and code patterns and everything to see how good or bad is the quality of your code. But instead of doing something like Sonar does, which is give you lots of information, what Better Code Hub does is it actually gives you 10 guidelines for you to follow. Let's see if we can find the guidelines. I think if we go to docs maybe or not, let's see, about guidelines.
15. Guidelines for Writing Code
The workshop mentions 10 guidelines for writing code. These guidelines cover writing short and simple units of code, writing code once, keeping unit interfaces small, separating concerns in modules, coupling architecture components loosely, keeping architecture components balanced, keeping the code base small, automating tasks, and writing clean code. The guidelines provide context and explain the benefits of following them. They emphasize the importance of small units of code for better understanding, reusability, and testing. The guidelines also offer suggestions for improving projects that do not meet all the criteria.
Yeah, it only mentions here. It gives you 10 guidelines, and one of the guidelines is actually that you have to have short files and all that stuff. So I highly recommend you to, let's see if I can actually sign in with GitHub to give you an example. And let me sip my water here because I've been talking a lot. So yeah, I have here a few projects that I have integrated with this too, and it's out of the context. But since all were mentioned, I wanted to talk about it. Let's have one that is five that has maybe a score of 10. Yeah, this one has a score of 10. So let's look into it. How do I look into it? I can go to View Results. So the 10 guidelines are write short units of code, write simple units of code, write code once, so the dry thing. Keep unit interfaces small. Separate concerns in modules. Couple architecture components loosely. Keep architecture components balanced. Keep your code base small. Automate your tasks and write clean code. The first four, they are about the way you write your code. The next four are about the way you architect your code. And the last two are about the ways you work. And if you click on each of them, you here have tabs, a tab for reading what the guideline is all about. And this one is the one that tells you. Small units are easier to understand, reuse and test. And so it gives you some context. And if your project is not 10 out of 10 as this one, it would give you some info about what could be improved and things like that. So that's my take from all this mentioned before, okay.
16. Versions 15-19: Tasks and Improvements
We are now on version 15 and will continue until version 21. After that, there will be a five-minute break for Q&A. In version 16, more tasks were added to the project. Version 17 allows running tests against a local environment. Version 18 improves task descriptions by including the protractor command in the title. Version 19 introduces the side-press plugin for simulating keyboard key presses.
So we are on version 15. Let's see how we are in terms of time. I think we can go into version 21. And then we can have five minutes' break so you can grab the water, do whatever you want, and then we come back for Q&A. What do you think? Leave your comments on the chat.
Let's go to version 16 because we are almost there. Version 16, I added just another bunch of tasks. I already had a bunch of tasks. You see, like, my task file is very short. My scenarios in the feature is getting very short. The feature is getting big. It has almost 200 lines, but it's still very to the point. It's just a few more entries into my array. They have title, snippet to migrate, migrated snippet, and the Cypress commands that should be available, okay? So very straightforward, just added a few more tasks.
Version 17, allow run tests against a local environment and document how. So this is where I added a session in the documentation. I don't know if I should have added this as a version, but since now you can also, I created this script, CyOpenLocal. Yeah, maybe it makes sense. Then I basically what I did is before I was having the URL inside of the site of visit command, instead I added the URL as a property of my config file in the base URL. Then I can simply visit slash. So the base URL is https://migrator.cypress.io. And then I do site of visit slash, which will basically have this slash in the end here right after the.io. And then I can have this script that runs the Cypress local or in headless mode, where I can overwrite the base URL with dash dash config base URL. Https://localhosts column 4200, okay? So, in case you come here to the Cypress migrator official project, you clone it, you install off the dependencies, you start up your local environment, and then you do the same, you clone the project from my repo, you can then run the task that I wrote against Cypress migrator in your local computer instead of the version that is running in production on migrator.cypress.io. This is what version 17 basically allows you to do. I decided to show you running version that is in the internet because it's much faster than the one when I run it locally, especially now that I am live streaming, streaming and, not streaming. But yeah, I'm sharing my screen, streaming my video and everything.
So let's go to version 18. In version 18 I decided to improve the tasks descriptions. And in here before in my features, I had the property title. But I had the Cypress commands. And I thought, you know what, I could have protractor command that could be used in my title. And then instead of having migrates protractors title, I would have migrates protractors, protractor command. Command, so if it was a side element by CSS, it would be my greats protractors element by CSS command into Cypress. And then the Cypress commands, since they are an array, I would just get the first one, so what would calculate the length of it, subtract one because an array starts on index zero and then say command. So it would be, for instance, my greats protractors element by CSS command into Cypress, cy.get command, something like that. And then I changed in my features, every place where I had title, I instead called it protractor command. And that's basically it all until the end. Okay? Version 19, we're almost there. Improve tests readability by using the side dash press plugin that I have created. Let me show you. NPM, this is the one. So if you go to npm.js, and you search for Cypress Press, you'll find this plugin that I have created, which as its description says, it simply adds a silly cypress.press command that simulates pressing the keyboard keys. You can NPM install it as a dev dependency. Afterwards, you can import it or require it in your support file. And then here is how you use it correctly and how you should not do it. So the idea is you can, for instance, let's say I'm testing Google or DuckDuckGo where I would have an input text, and instead of clicking the magnifying glass button, I would just type and press enter to run my search. This is how I would do using this plugin. I get an input of type text. I type cypress.io and then I press enter. And the available keys are described here. They are the exact same as in the official Cypress documentation. So here are all the available options. You can see backspace, press down arrow and so on and so forth. Plus, I have added also, you can say press ctrl A, upper case or lower case. And the same for comment if you are Mac user, for instance. Here are examples that use this library. I have actually had this one in here because it doesn't use it as well. And I just wanted to show you because this is what this commit does. So this commit, I installed Mylib on version 1.0.2. Because I installed it, I added it to the ntwm.js file inside of the support directory. And now instead of doing type, bracket, select all, I simply say dot press, select all. Which would do this thing here. It would do something like this. Selects everything that is in there so that I can clear afterwards. So just to make it a little bit easier to read. So, what does it mean? Type, select all between curly braces, right? It's kind of weird if you don't... If you don't know exactly what this does in Cypress. But if you say dot press select all, you would understand it better. So this...
17. Versions 20-21: Href Assertion and Code Shortening
In version 20, an extra assertion was added to test the correctness of the href attribute in the API details section. The command is transformed to remove the sci, dot, and parentheses, and the href is checked against the transformed command. In version 21, the test code and variable names were shortened for readability and conciseness. A five-minute break is taken, and a question from Olga is addressed regarding the use of index in iterating over the Cypress commands.
This command, again, the Boy Scout rule. Leave the campground cleaner than you found it. I just left it a little bit easier to read the task itself.
Let's go to version 20, the one before last. Now I added an extra assertion on the API details section. To test that anchors, hrefs are correct. So if you remember when I executed all the tasks, I was not only checking that the side contains here and the site of click is here, but also that it has the correct href. So if we inspect here, we'll see that. Let's see. Where is it? Yeah. Here is the anchor. It has an href to on dot cypress dot IO slash click and this other one here has to on dot cypress dot IO dot contains slash contains, which redirects us to docs dot cypress dot IO slash API slash commands slash contains. Okay? So in this version 20, I decided, you know what, I'm not testing this specific thing and I think this is important, so let's add a test for that. Not add a test, add an extra assertion in the same test that we already have for that. So what am I doing here? Now the only change was in the test file itself, so I'm still iterating over all the cypress commands, which are those arrays inside of my fixture. So I have the test scenario's fixture. Each test scenario is an object which has a protractor command, snippet to migrate, migrated snippet, and the cypress commands. These are the cypress commands, so I'm iterating over each of them, but now instead of getting just the command, because I want to check that the command is displayed in the page, I will also want to check that the href of that specific anchor is correct. So before I was using, my callback function was receiving just one argument and then I can omit the parentheses. Now that my error function is receiving two arguments, the command and the index, I have to put the parentheses in place. And I'm getting the index because I will need the index, right? So, first I am doing a transformation here where I'm getting the command of that specific index. So in the first iteration will be the command in the index 0, which is the first command. In the second iteration, the command in the index 1, which is the second command, and so on and so forth. I'm basically getting this command and I'm replacing. So the command is... Let me open in here. So the command is something like this, right? Sci.get or dot submit. This is a command. This is another command. In some cases, there is the sci in front and some there are not. So I'm basically saying, if you find a sci, replace it for an empty string. If you find a dot, replace it for an empty string. And if you find a parenthesis, replace it with an empty string. In the end, if we replace this, the dot and the parenthesis will get just get. In this one, if we replace the dot and the parentheses, we'll get just submit. And this is exactly what I need because the command itself is sci.contains, sci.click, but the href is just contains or just click. And this is what I need, and this is why I'm doing this transformation. So I'm basically transforming the command to have just the command itself without the sci, without the dot and without the parenthesis. Then I get the API details div. I still assert that it should contain my command, which in this case, it's not the transformed version, is the sci.get, an open and closed parenthesis. But from this element, I find the anchor, so I find the a. Then I will find if there are more than one, for instance like this, it will say, okay, I found two. Which one do you want? Get the one with the index that I am on, which if I am in the first iteration, it will get the one with index zero. If I am in the second, the one with index one. If I am in third iteration, it will get the one with index two, and so on and so forth. And then assert that it should have the href, it should have the attribute href with the value HTTP column slash slash open on.cypress.io slash my transform command, Cypress command which is just the contains or get or visit without the site. And the parenthesis at the end. So, with this one, I added this missing verification that I needed to make sure that the application works as it should. And then we have version 21 where I basically shortened the test code and the variable name a little bit. So, in many places I was breaking the line. Sci.get- left-side editor.type in the next line. And I felt, you know what? It would still be short if it was just in one line and it would make my code a little bit shorter as well because now instead of two lines, I have one. Same for the click. So, I decided to do that. So, have the click just after here and the type just after here in the same line. It's still very easy to read, easy to follow. I like to break it down in many when I'm doing something like that, where I'm changing more than one command. But if I'm changing just one and the first command is still short, it's maybe better just to have it all in line. And I also changed, shortened a little bit the name of my variable here to instead of transformed cypress cmd from command, transformed sci cmd. So, I'm not reducing number of lines here, just making my variable name a little bit shorter, still easy to follow, easy to read. And that's version 21.
As I said I would, I think we could have like a small break of five minutes. I'm just gonna stop sharing for a little bit. I'm gonna mute my mic and mute my camera. Let's go have some water. Some like five minutes rest time, and we'll be back in five minutes. All right? See you soon. So, hope you had some time to grab a water, a soda, or, you know, just to hydrate yourself a little bit or to stretch a little bit. We have a question here from Olga, where she said, you actually... Well, she said, why using index if you can use Sci.Contains? And I think you were mentioning about what is in my screen at the moment where I am iterating over the Cypress commands and getting out only the command, but also the index.
18. Using Index for Assertion and Workshop Takeaway
I explained the need for using the index when asserting on the href attribute of an element. There was a question about the workshop recording, which will be available on the GitNation portal. I discussed the changes made to the code and the GitHub Actions workflow. I also mentioned the upgrade to Cypress 10.11.0.1 and the addition of a badge to show the task status. The takeaway from the workshop is to start simple and add or remove complexity as needed.
And actually, I do need the index, Olga, because I'm not asserting on the content of the HTML tag, and instead I'm running an assertion on a specific attribute of that element, which is the href attribute. And so I need to get the div with the data tests, API details. And then from there I have to find the anchor element. So this is a div. Inside this div, there's lots of elements. Some of them are anchor tags, which are links, basically. Then I have to say, and then when I find the links, I might find, as I showed you here, more than one. So I need to know which one do I want to check for the href attribute. Is it the first? Is it the last? So I do have to say, find the a that has the index equal to one, and then I will say, now I'm looking into this one. See if the href is correct. Second iteration, see if the href of the second one is correct. So that site contains wouldn't work. So it contains just for the content of the tag, not for the attributes of the HTML elements. Okay. And then August said something else. So you said here, I can't understand why there could be several anchors. It's exactly what I said before. Let's do it this way. So I have this selector here, right? Let's come here. Let's go to the console. It's the document, dot query, selector. And let's pass this in here. So I get this element here, okay? If I enter, I get this div, okay? If instead of that I say, inside of this div, get the A, it will get just the first one, but site.get actually works like query selector all. Where if there is more than one element query, so let's see, what did I did wrong? Query selector all. And now it's returned an array where the first item of the array is the first A and the second item of the array is the second A. So it does return an array of elements, and because it does return an array of elements, I need to use the index to know from which element I want to check for the href. I hope that makes it clear for you to understand all.
Let's see, we have a question or a comment, let's see, a question from Fernanda. Hi, Romier, thanks for the workshop. Question, will the workshop recording be shared afterwards? Yes, it's going to be available, as Sasha just mentioned in the chat in the GitNation portal in a few days, okay? So yeah, if you arrived late and you weren't able to get all the content, you have access to all of it afterwards, so don't worry. Let's see, there's someone that just arrived, so welcome to someone that just arrived. Hope you have a good time. We are kind of more close to the end, but still, as I just said, it's being recorded, it's going to be available afterwards, so don't worry.
So after version 21, there are no more versions, it's more like I fixed... when I started coding, if I would change... if I would type Cy.getSelector.ContainsSampleContent, I would get... well actually, if I would type Elements by CSS containing text, the Selector and then the text, I would get before a Cy.getSelector.ContainsSampleContent. But I created an issue. I can actually show you the issue that I have created. So, if you go to Issues, and we go to Close to Issues, it's the only one that was closed. This was open by me, as you can see here. I said, when migrating something like this, so, I want to migrate Elements by CSS containing text, the Selector is.pet or a class pet, and the content is Dog, it generates Cy.get.pet.ContainsDog. And I said what about migrating it as just a Cy.contains that would receive true arguments, the first one the CSS Selector and the second one, the text itself, and Cy.containingText receives true arguments. Cy.contains also can do it. And then someone liked the idea and decided to implement this change. So, there was a PR pull request to change it, and this is why when the fix came, I tested again against the production environment. The tests that were checking Cy.get.contains were failing because now there was just Cy.contains, so I updated them to be just Cy.contains the selector and the text, and then I updated of course the Cypress commands as well because here I wouldn't have anymore Cy.get and the Cy.contains and instead I would just have the Cy.contains. So I updated a few tests here, then I upgraded to version 10.10, and what else? I removed some Cypress config, then I added a GitHub Action workflow where I basically copied the basic workflow from the documentation, and now if we go here, we see that for every push that I do, there is a GitHub Action that runs the tests, and if we click here, we can see the test results in a terminal like on GitHub where we can see here Cypress Migrator migrates protractor browser.getCMD into Cypress SCI.VISIT command. Migrates protractors browser.getTitle command into Cypress SCI.TITLE command and so on and so forth. So now this is not only, I'm not just able to run these tests against a remote environment or a local environment in my machine, but I can also run the tests against the production environment using GitHub Actions in a continuous integration workflow. What else? Let's see here in the commits. After I added the GitHub workflow, I decided to add a badge in the readme to show that the tasks are passing. And I've been upgrading from Cypress 10. We started with Cypress 10.4, then Cypress 10.10, then Cypress 10.11, and today I upgraded it to Cypress 10.11.0.1. And I added this takeaway markdown file. So the badge that I was mentioning is here. So now if the tasks failed, this will turn into red. Luckily, everything is working. Not luckily. It's because it's actually working, right? So we have this nice badge here. And I would love to answer some questions if you have more. But I would like to go this takeaways.markdown file here that I created because I want you to get out of this workshop with this thing here. So start simple and add or remove complexity on demand. I review code of a lot of people. I train people into test automation with Cypress and I did that with other tools as well, with Protractor in the past, with Backstop.js and other frameworks as well. And I see people because they were used to do things in one way, for instance, let's use page objects, which is something that they don't recommend in the Cypress world because there are many other options that we could use. And I don't like page objects also because it over tests your UI. You go through the UI to the same place again and again, when you should actually be creating more optimized tasks, as I like to call them. So start simple, as simple as possible, even if you have to add some duplications in the beginning, as you saw in version one of my commit. And evolve it on demand.
19. Code Complexity, Communication, and Testing
20. Test File Setup and Page Objects
I had a test file of 100 lines with five test cases, but now it's less than 55 lines with 31 test cases. I achieved this by extracting scenarios to fixtures. I use the describe function for the Cypress migrator, with a before each to set up the tests. I get the text areas, clear them, and perform actions. I iterate over scenarios and run specific tests. I check assertions and iterate over Cypress commands. I replace certain characters and check API details. I find anchor tags and check their attributes. Page objects can lead to over-testing, as they duplicate steps already tested.
So in the beginning, I had a test file that was, I think, 100 lines of code, and I had only five test cases. Now I have a test file of less than 55 lines, and I have 31 test cases. I was able to achieve that by extracting my scenarios to fixtures, and because I am exporting them, I can import them in my test file with the import keyword.
Then I have my describe function for the Cypress migrator, which receives a callback function. In the callback function I have the before each with all the steps that are needed before every test start, so every test starts in a clean state and an exact same state as the previous so we don't have dependencies between the tests, where in the before each a visit slash, which will concatenate slash with the URL defined as a base URL on my config file. I get the text areas that are inside a div which has both the vias and the side-by-side classes and I give to these text areas an alias of text areas. Then I get the first one, which is the one in the left, the one that I can interact with, the one that I can clear and type. I press select all to select all the content and I clear it. And in some very rare cases, it might not clean everything. And so I will change a dot then, which will receive a callback function. For this callback function, I'll pass the text area from the left side as an argument. And I'll say, if there is some value for this text area, log in the console that you are clearing it again and do clear it again by wrapping the text area and changing a dot clear. And then finally, I give to it an alias of left side editor.
And then I have, I get my text. I use the text test scenarios that I imported from my fixtures on line one. I get them on line 20 and I iterate over each scenario. Because I have an array, I can iterate over each element of that array, basically each object in that array in this specific case. And then for the for each function, I pass a callback function as an argument. This callback function receives as arguments each of the scenarios, meaning each of the objects. Each object will have a protractor command, a snippet to migrate attribute, a migrated snippet, and a cypress commands property, which in this case will be another array. I'm basically this structuring all these properties from the scenario as variables so I can access them inside of this for each context. Context. Alright, and then I am creating a new block inside of this for each, meaning that for each scenario defined in my feature, I will have a specific test, specific test, which I use here to play with others, to print in the Cypress console, my grades protractors, protractor command, command into Cypress, and then I'll get the first command from the list of commands in my array. And then I do a sylab get for the left side editor, and I type the snippet that I got in here. I do a sylab contains for a button that contains the text, Migrate to Cyprus, and I click on it. I get all the text areas. And now instead of getting the one in the right, I want to get… instead of getting the one in the left, I want to get the one in the right. So I say.last to get the last one which is the one in the right. I scroll it into the view so I can see what is in there and I run my assertion that it should have the migrated snippet as the value of that text area. And I also, besides this verification here, I also iterate over each of the Cyprus commands which are in my fixtures, and because I need not only the command, but also the index. In my arrow function I encapsulate them into... I have to wrap them into parentheses. I transform the Cyprus command of that specific index. So in the first iteration, the one on index zero, in the second iteration, the one with index one, and so on and so forth. I replace si by nothing by an empty string. I replace dot by an empty string and I replace parentheses by an empty string. And then I get the API details div. I check that it should contain the command, not the transformed one. So, si.get, si.visit, and blah, blah, blah. And then from these elements here I find all the anchor tags. It might find more than one, so I say, find the one with index 0, or 1, or 2, and so forth, and check that it should have the attribute href on.cypress.io, slash, the transformed command, which is the command without the site, without the dot, and without the parentheses. Let's run the tests once again, so you can see them running. And let me see if there is another question here. Let's see. Okay. Okay, so that was great. Thank you. Thank you, Dennis. The way you shortened code, it was very interesting. Thank you. Could you advise something about pros and cons of page objects? I still like them in large projects since they allow IntelliSense. I surely can't give my opinion, Olga, on page objects. I kind of gave it before, but I'm going to say it again. What's my impression of page objects? Page objects, they were a pattern used in test automation for you to abstract some ideas, so abstract the definition of elements and how you interact with the elements in another module, which could be a class, for instance, that you could instantiate new instances of that class, and then if you have many different tasks or even many different test files that need to reuse those same elements and actions, you wouldn't need to duplicate them all. What is the problem? That's okay, then you are following the drive, don't repeat yourself, right? But there is one very big problem on doing this, and the very big problem is that you are over-testing your application. I'll give you an example. Let's say we want to test, I don't know. Let's say we want to test Typeform, for instance, which is a product that I worked for, for this company, right? So it's a product where you can create very nice forms so you don't collect leads, for instance. I want to test that I can log in. So I would have a test case that would visit the login page of Typeform, it would type my user, it would type my password, it would click in the Login button, and it would wait for the login to happen and assert that I was successfully logged in. Great. Now I need to create another test case, and this test case is that I can create a Typeform. But as a precondition of creating a Typeform, I need to log in. I need to be signed up, I need to be logged in, basically, right? I cannot create a form if I'm not logged in before. So I would have to repeat the steps of logging in so I reach the state where I can now create a form. And then I would click Visit the page that allows me to create a form, or click a button that will direct me to a page. And then I will interact with the fields in that form to create and check that the form was created. But I have already tested logging through the graphical user interface once. So doing all the same steps of signing up, of logging in through the UI for the test of creating a form is a waste. I'm over-testing and passing again through the same, like the application is executing the same lines of code again and again, when they actually have already tested that thing.
21. Optimizing Tasks and Refactoring
If the front end is broken, you can still authenticate through an API call and test other scenarios that don't depend on the front end. Abstractions like page objects are not needed in this case. There are many options for optimizing tasks, such as using app actions or mocking API calls with static files. Cypress also allows running tasks from the command line using the side up exec command. The goals of refactoring include simplicity, maintenance, and code understandability. Understanding HTML, CSS selectors, and the programming language used in the framework is crucial for writing robust code. Component testing is also covered in the speaker's YouTube content.
22. Cypress Courses and Documentation
Feel free to visit my website and check out my courses on Cypress. I offer courses in both English and Portuguese, covering different levels and topics. The Cypress documentation is comprehensive and worth exploring. It's important to understand that Cypress has a different architecture and approach compared to other test frameworks. Page objects may not be the best fit for Cypress. When it comes to logging in through the UI, consider using the session command to cache the session and avoid repetitive logins. The Cypress documentation provides more details on this feature.
And yeah, feel free to take a look at this one. My website is Valmir.dev, this one here. I'm going to send it in the chat so you can have access to it. Then in the header, you can go to YouTube. You'll be directed to the English content chat, and then you can find this one about component testing. If you speak Portuguese, you can find in another playlist also one in Portuguese, which is kind of the same as this one, but in Portuguese.
Let's see what else. Why do you use side of gets data? Something should contain command find a equal index and outside of contains. Data something command. Let me see if I understood it. So in here index. data tab command. It's okay I got it. So you are saying that in here instead of doing find a index I should do data test blah blah blah. Comma. And then d command. It's because I'm running two assertions in just one chain of command. The only thing I'm doing here is just checking that the API that they use have the command which is PSY.VISIT open and close parentheses. But from there I can still when I'm in this should I still have access to the PSY.get. So from this one I can chain it to find all the anchors from them I can get the one in the first index, the one in the second index and then verify that the hrf attribute has the correct value. This is why.
Project Olga, downloading your computer. Try what you think it will work. And you see that it will not work because I'm writing two assertions in one chain of commands actually. I hope that makes it clear. About page objects, your login page sample is about misusage of page objects. Not sure. Not sure. The point with page objects is that, that's my impression. My impression is that people learned how to do test automation of graphical user interfaces in a way before Cypress. And then they tried to apply the same concepts to Cypress. When they should not, and they should not because I present very, very, very different than the other test frameworks that we had before. So, what I like to say is that when you are coming to the Cypress world, to the Cypress side of the force, you should actually forget some of the concepts that you have learned, and apply the new ones with the new architecture and with the new power that Cypress gives to you. If you try, it's the same as simply trying to translate to protecting from Cypress. It's not how it should be. There are new things in Cypress land that didn't exist in Selenium, for instance, and if we try to apply the same concepts, they won't work greatly in the Cypress land because Cypress is completely different. It has a completely different architecture and a completely different way of writing tasks. That's my take here. I would disagree with this example of Page Logic is valid. It's okay to disagree. Don't worry, gitlab's testing sounds interesting.
Feel free to go to my website here and go to courses. In courses, you find my Udemy profile. I have courses of Cypress in Portuguese, but I also have the intermediate course in English. The course, the basic one in English here, and the one of visual regression testing with Cypress and Percy. The ones with the US flag are in English. The ones with the Brazilian flag are in Portuguese. So in English here, we see that I have the Advanced, Intermediate, and Basic. In Portuguese, I have more than three. Besides the Basic, Intermediate, and Advanced, I have also Good Practice, Test, and so on. And also the visual regression testing, which is available in both languages. Let's see. What else? Logging page sample is about don't do test preconditions by AUI. There are backend requests and DB requests for this. Yeah, actually, I can deploy UI without backend to some environment just to test that's happening if backend isn't available. Correct. I log in through the UI because the login is with third-party app, WordPress. Do you think this could be possible without the UI? It depends on the APIs that WordPress will provide you with. So it depends really on the authentication provider that you were using. There are, for instance, ways to authenticate with Google without going through the UI if you are using Google as an authentication provider. I don't know about WordPress, but even if you need to go through the UI, then I would recommend you to learn about a session command, which is a functionality that was introduced not very long ago which allows you to cache the session and you can even share this session across the spec files, and this allows you to log in through the UI only once and then cache the session. And when you try to execute the site of logging again, it will ask Cypress, do I already have a session in the cache? Oh yeah, you already have. So, restore the session for me and then you reach the application already logged in without having to go through the UI more than once. So, I would highly recommend going through that. Cypress documentation is great. When I first discovered Cypress in 2018, I kind of, I read it all. Nowadays it would be very difficult to read it all because it's much more dense than it was. But it was already dense. But I felt so impressed about the way you can write tasks with Cypress that I decided to read all I could. So, I spent a few nights reading the documentation and this is why I say that Cypress is very different because I really read all the documentation and I know its details and I know that you can do things differently than you were used to.
23. Cypress for UI and API Testing
August, Cythout contains... Well, feel free to do what you're mentioning there, Augh, and work my project, create a PR and let's see if it works. I would be glad to review your code and have it merged if it works great and if it's simpler than what I showed you.
Marcos, would you recommend doing API tasks with UI tasks or should they be separate? I recommend both. I recommend you run your graphical user interface tasks with Cypress. You can also run your API tasks with Cypress with the site that requests. There's even a nice plugin that was created by another Cypress ambassador called Philipp, which is the Cypress plugin API, which allows you to see what is going on in the API. When you are running a site request, you don't see anything here on the right. This plugin allows you to see which verb was used, what was the URL, what was the status of the request, and you can see the response, the headers and cookies and everything. I actually have done two live codings on my YouTube channel in Portuguese unfortunately for you that don't speak Portuguese about this plugin here. So I recommend using Cypress both for end-to-end testing, for API testing. You can use it for component testing as well. And if you do use for API testing and you use for UI testing, you can also mix them both to create what I call optimized tests, where you do all the preconditions through API calls. So you don't waste time doing everything through the UI. You don't waste time over testing your app. And then you do through the UI only what you need to do through the UI. So I highly recommend doing that, Marcus. Hope that answered your question. Any more questions? We still have 25 minutes. So if you have more questions, we can keep going for a little bit more. If not, we can wrap it up. Up to you. What do you say?
Thank you. Thank you, Clary. Thank you, everyone who attended the workshop. I hope it was helpful information. And if you didn't understand it all, don't worry. The recording will be available soon so you can watch it again. And because it's been recorded, you not only will be able to watch it again, but you will be able to pause if you are in doubts of something, and then you can go back and forth. So if you want to watch with a faster speed, you can do lower speed as well. So wherever you prefer when the recording is available.
24. Tester Experiences and Recommendations
Filip and I discussed the need for Page Objects in Cypress. I recommend watching Filip's talk on Full Circle Testing With Cypress. Cypress ambassador Philip offers a free course on Advanced Cypress. Cypress officially recommends not using page objects, but it depends on your needs. Over-testing your application can be a smell. Consider better ways of testing without repeating UI interactions. Maria found an article on ApliTools blog. Thank you all for attending the workshop. The recording will be available soon. I enjoyed giving the workshop and hope you learned something new. See you next year at Test.js Summit.
Stories and experiences of a tester and Cypress ambassador. I highly recommend you watching this video here, where in a moment, in a specific moment in the video, Filip and I, we discussed this, the need of Page Objects and how you can still achieve great things with Cypress. And recently I watched a talk, actually, it was on Test.js Summit, I watched history, I watched this talk here from Filip, Full Circle Testing With Cypress, Filip, create Test.js Summit 2022. He doesn't mention necessarily Page Objects, but it's a content that I highly recommend because you understand the kinds of things you can do with Cypress that maybe would help you understand why Page Objects would not be that necessary when we are talking about Cypress Tests.
Let's see what else. Have you used Actions in any of your projects? Yes, I did in some projects, especially at work, and so they are not like public repositories. I can show you them. But there is one course in Test Automation University. Automation University. Automation. Automation University. There's a course called Advanced Cypress, something. Let's see. Cypress. This course here, it's for free from Philip, Cypress ambassador, learn advanced concepts in Cypress. He teaches very briefly actions, if I'm not mistaken, in this course, highly recommended as well.
Maria, from what I read, it's just official Cypress recommendations to not use page objects, but it just depends on what you want to do and how you write tasks. People do use page objects with Cypress. I'm not telling you you should not use it. If I don't use it, I don't recommend using it because I think there are different ways and better ways of writing Cypress tasks. But I don't discourage you. If you think that works for you, it's fine. What I recommend is that you don't overuse, over test your application. If you do over test, then it's a smell that, let's say it's a smell that you could improve something in your test suite. You could optimize some state creation in your application somehow. And then I think you should try to address those. Also, from my understanding, when they are in one place, maybe I'm doing something wrong, of course. Yeah, again, Maria, it's what I said. The thing is that, if you are having to store selectors and actions for your page in a different file because you are using the same selectors over and over in different test files or in different test cases, there is a smell that you are over-testing your application. So you should think about better ways of testing your application without having to go through the user interface again and again for things that you have already tested once. That's the point. Yeah, Maria also found an article on ApliTools blog. I like the article about the question. So thanks for sharing that, Maria. Is there any other question before we finish this? I'm going to stop sharing so we can see some and see what's going on in here. We put this in here. Let me open the chat once again. It seems that we have no more questions so I want to thank you everyone for being here today. If you haven't had the chance to watch it all, don't worry. It's been recorded. It's going to be available soon to you. I pretty much liked giving these workshops to all of you. I hope you liked it too and I hope you learned at least one new thing that will improve the way you write your tasks. And yeah, thanks a lot. See you next year, probably on Test.js Summit. I'll be there for sure and I hope you are there as well.