Cypress Assertion helps us to assert a particular Assertions are validation steps that ensures whether the expected result is equal to the actual result. In test automation, we assert a statement to verify that the test is generating the expected result. If the assertion fails, then the test case fails ensuring that there is a bug. In this article, we will discuss about Cypress Assertion with Handson implementation and examples.
Cypress uses and wraps Chai assertion library and extensions like Sinon and JQuery. Cypress automatically waits and retries until the assertion is resolved. Assertions can be used to describe how the application should look like. We can use Cypress assertions with combination of waits, retry, block until it reaches the desired state.
Cypress Assert Text
In general English, we would describe an assertion something like, I would expect the button to have login text. The same assertion can be written in Cypress as
cy.get('button').should('have.value', 'login')
The above assertion will pass if the button has ‘login’ value.
Cypress Common Assertions
There are a set of common Cypress assertion that we use in our test cases. We will be using them with .should() . Let us look into the use case and examples.
Some of the common Cypress assertion are listed below
Length
Value
Text Context
Class
Existence
CSS
Visibility
State
Disabled Property
Cypress Length Assertion
length() will check if the particular element has length
cy.get('dropdown').should('have.length', 5)
Cypress Value Assertion
The Cypress value will assert if the particular element has the expected value
Cypress Visibility Assertion asserts whether the DOM element is visible in the UI
cy.get('#form-submit').should('be.visible')
Cypress State Assertion
Asserts the state of the DOM element
cy.get(':radio').should('be.checked')
Cypress Disabled Property Assertion
Cypress Disabled property assertion asserts whether the element is disabled
cy.get('#example-input').should('be.disabled')
Cypress Retry Assertion
A single command followed with an assertion will execute in order. Initially, the command executes and then the assertion will get executed. A single command followed by multiple assertions will also execute in order – first and second assertion respectively. So when the first assertion passes, the first and the second assertion will be executed along with the commands again.
For example, the below command contains both .should() and .and() assertion commands, where .and() is otherwise known as .should()
In this section, we will discuss on the different types of assertions in Cypress such as
Implicit Assertion
Explicit Assertion
We will look into detail on both the types with examples
Implicit Assertion in Cypress
In implicit assertion, we use .should() or .and() commands. These assertion commands apply to the currently yielded subject in the chain of commands. They are dependant on the previously yielded subject.
We will look into an example on how to use .should() or .and() commands
cy.get('button').should('have.class', 'enabled')
With .and() which is an alias of .should() ,we can chain multiple assertions. These commands are more readable.
The above example is chained with .should() stating it should have the class “active”, followed by .and() is executed against the same command. This is very helpful when we want to assert multiple commands.
Explicit Assertion in Cypress
Passing explicit subject in the assertions falls under the explicit type of Cypress assertion. Here, we will use expect and assert commands as assertion. Explicit assertions are used when we want to use multiple assertions for the same subject. We also use explicit assertion in Cypress when we want to do custom logic prior making the assertion.
expect(true).to.be.true //checks for a boolean
expect(object).to.equal(object)
Negative Cypress Assertion
Similar to positive assertions, there are negative assertion in Cypress. We will be using “not” keyword added to the prefix of the assertion statement. Let us see an example of negative assertion
cy.get('#loading').should('not.be.visible')
Negative assertion is recommended only in cases to verify that a particular condition is no longer available after a specific action is performed by the application.
For example, let us consider that a toggle is checked and verify that it has been removed
// at first the item is marked completed
cy.contains('li.todo', 'Write tests')
.should('have.class', 'completed')
.find('.toggle')
.click()
// the CSS class has been removed
cy.contains('li.todo', 'Write tests').should('not.have.class', 'completed')
Cypress Custom Assertion Message
With Cypress, we can provide additional information or custom message for assertions by using a library of matchers. Matchers comprises of small functions that differentiate values and will throw detailed error message. Chai assertion library will help our code look more readable and test failure very useful
const expect = require('chai').expect
it('checks a number', () => {
const value = 10
const expected = 3
expect(value).to.equal(expected)
})
Cypress Custom Error message
Cypress Assertion Best Practices
We can write multiple assertions in a single block by using a chain of commands. It is not necessary to write single assertion like in unit tests. Many write assertions like below. It is okay to write in that manner, but it increases the line of code and redundancy.
describe('my form', () => {
before(() => {
cy.visit('/users/new')
cy.get('#first').type('ashok')
})
it('has validation attribute', () => {
cy.get('#first').should('have.attr', 'data-validation', 'required') // asserting whether the #first has required field
})
it('has active class', () => {
cy.get('#first').should('have.class', 'active') // asserting whether the #first has active class
})
it('has formatted first name', () => {
cy.get('#first').should('have.value', 'Ashok') // asserting whether the #first has capitalized first letter
})
})
As you see above, the same selector and assertion type is getting repeated. Instead, we can chain these commands in one single assertion which performs all the checks in a linear fashion.
As mentioned above, we can chain the single selector with multiple assertions! This is one of the recommended best practices of writing assertion in Cypress.
To understand about the Page Object Model in Cypress, click here.
One of the best practices in test automation is separating the test data from the test files. This aspect is one of the primary requirement while designing test framework. Cypress helps us the abilities to separate the test data with CypressFixtures. In this topic, we will be discussing about Cypress fixtures with hands-on implementation and real time examples
Cypress Fixtures can be used source data from external files. Fixtures in Cypress help you to read from or write into files. One of the popular framework in test automation is Data-driven framework where we separate data from the test files. We usually keep the data in external file like Excel and read them using external libraries. Cypress provides us the same feature to read data from files.
Cypress provides us a folder called fixtures, where we can create JSON files and read data from it where we can read those files in multiple test files. We will store the data as key-value pair and access them.
How to use Cypress Fixtures in Tests?
We can access Cypress fixtures via the following syntax given below
We will understand the parameters that can be passed in fixtures
filepath – the path to where you have stored your test data
encoding – The encoding that are used while using a file. Some of the encodings are ascii, base64,hex,binary etc
options – In options, we can pass the timeout response. It is to specify the timeout to resolve cy.fixture()
How to read data from Fixtures in Cypress?
We will be defining the test data in a file under the fixture folder. We will be accessing the test data from the JSON file in the test script using Cypress fixtures.
Now, let us undertand an example for Cypress fixtures. We will be logging in the url using the username and password. So let us store the username and password values in a file.
Let us create a file named credentials.json under the fixture folder. We will be defining the variables in JSON format.
In the above example, we are accessing our JSON file via cy.fixture(‘credentials’). Since our JSON file name is credentials.json, we are passing the file name in cy.fixture(). Now we are using alias concept and defining our data as testdata. With the variable testdata, we can use the values of username and password in our test file
As you can see above, in .type() we are passing the value from our credentials.json file as this.testdata.username. Similarly, for password we are accessing the value using this.testdata.password. For the url, we are using the same way as username and password.
When we run the test case, you can see the value getting printed in the dashboard. This way, we have executed our test case using Cypress Fixtures
Fixture test result
Cypress Multiple Fixtures
In this section, we will understand how to use Cypress Fixtures with multiple fixture files.
If we want to use different fixture data for the same test file, for example, there are two set of credentials we need to verify for the login page, how can we access the files?
One way is to write multiple it blocks which will replicate the same code again and again. The other way, we can use Cypress fixtures to access different test data in the spec file. Let us see how we can achieve that using Cypress fixtures
We already have a fixture file called credentials.json.
Now let us see how we can access the two different data in our test file.
We will refactor the same test file using the condition of using two different fixture files.
const testValueFixtures = [
{
"name": "credentials",
"context": "1"
},
{
"name": "userData",
"context": "2"
}
]
describe('Automation Test Suite - Fixtures', function () {
//looping through both the fixtues
testValueFixtures.forEach((fixtureData) => {
describe(fixtureData.context, () => {
// accessing the test data from the fixture file
before(function () {
cy.fixture(fixtureData.name).then(function (testData) {
this.testData = testData;
})
})
it("login", function () {
cy.visit('https://admin-demo.nopcommerce.com/admin/')
cy.get('[id=Email]').clear()
cy.get('[id=Email]').type(this.testData.username)
cy.get('[id=Password]').clear()
cy.get('[id=Password]').type(this.testData.password)
cy.get('[type=submit]').click();
cy.url().should('be.equal', this.testData.adminUrl)
})
})
})
})
Accessing two fixture data example
Initially, we are creating a variable called testValueFixtures as an array where we are creating the context of two fixture files. In the first context, we are passing the name as ‘credentials‘ and the second as ‘userData‘ , as they represent our JSON file names where we have our value defined.
Secondly, we are looping through the both the fixture variables in describe block. And as we discussed previously, we are accessing the data in before block using .this()
The rest of the code is the same, where we are passing the data in the cy.get()
When we execute our test, it will run in two sets where the first case passes with valid credentials and the second fails due to invalid credentials
Fixture using the first fixture file
As you can see above in the snapshot, the first test case has passed and it has entered the value from the first fixture file credentials.json
Fixture example using the second fixture file
As you can see in the above screenshot, the test has failed and the values passed are from the second fixture file userData.json
You can also view how to write Cypress fixtures using the Page Object Model here
Cypress provides us API’s and methods to interact with the UI of the application. They are known as Cypress Commands and helps with the interaction of the web application. All the commands that are available have in-built methods and we will only invoke the methods in our test cases. The Cypress commands will simulate a similar action to an user trying to perform operations on the application.
UI Interaction Commands provided by Cypress
There are different commands provided by Cypress that interact with the UI. We will look into the list of all the commands in detail.
.click()
.dblclick()
.rightclick()
.type()
.clear()
.check()
.uncheck()
.select()
.trigger()
Cypress Click Command
.click() – This command is to click any element on the DOM.
As you can see above, the click accepts parameters like options, position, and coordinates.
Options
The possible options that can be passed to click are
Option
Default
Description
altKey
false
Switch on the Alternate key (Option Key in Mac), as optionKey
ctrlKey
false
Switch on the control key. Also known as: controlKey.
metaKey
false
Actuates the meta key (Windows key in Windows or command key in Mac). Also: commandKey, cmdKey.
shiftKey
false
Actuates the shift key
log
true
Prints the logs in the command line
force
false
This option forces the action and disables the wait for actionability
multiple
false
Sequentially click multiple elements
timeout
defaultCommandTimeout
Time for .click() wait before resolving the time out
waitForAnimations
waitForAnimations
Option to wait for the elements to complete animating before executing the command
Options in Click
Positions
The different types of positions that can be passed to .click() are
center (default)
left
right
top
topLeft
topRight
bottom
bottomLeft
bottomRight
Example
cy.get('btn').click() //clicking the button
cy.get('btn').click({ force: true }) //clicking the button by passing the option 'force' as true
cy.get('btn').click('bottomRight') // clicking the button at the botton right position
cy.get('btn').click(10, 70, { force: true }) // clicking the button with position value and force true
Cypress Double Click Command
Double click can be achieved by using dblclick() syntax in Cypress.
Syntax
.dblclick()
.dblclick(position)
.dblclick(x, y)
.dblclick(options)
.dblclick(position, options)
.dblclick(x, y, options)
Options
.dblclick() accepts all the options that are accepted by .click(). You can find the options in the above section.
Positions
All the possible positions that are specified in .click() are also available for dblclick(). The list of the positions can be found in the above section.
Example
cy.get('button').dblclick() // Double click on button
cy.focused().dblclick() // Double click on element with focus
cy.contains('Home').dblclick() // Double click on first element containing 'Home'
cy.get('button').dblclick('top') // Double click on the button on top position
cy.get('button').dblclick(30, 10) // Double click on the coordinates of 30 and 10
Cypress Right Click Command
This Cypress command, right clicks the DOM element .rightclick() command will not open context menus of the browser.rightclick() is used to test handling of right click related events in the application such as contextmenu.
Syntax
.rightclick()
.rightclick(position)
.rightclick(options)
.rightclick(position, options)
.rightclick(x, y)
.rightclick(x, y, options)
Options
As we saw above, all the options that are accepted by .click() command can be configured with .rightclick() command too.
Positions
All the possible positions that can be passed to the .rightclick() is same as the .click() mentioned above.
Example
cy.get('.welcome').rightclick() // Right click on .welcome
cy.focused().rightclick() // Right click on element with focus
cy.contains('January').rightclick() // Right click on first element containing 'January'
cy.get('button').dblclick('topRight') // Double click on the button on top right position
cy.get('button').dblclick(80, 20) // Double click on the coordinates of 80 and 20
Cypress Type Command
.type() command enters value into a DOM element.
Syntax
.type(text)
.type(text, options)
Arguments
.type() accepts string as an argument. Values passed to .type() can include any of the special character sequences given below.
Sequence
Notes
{{}
Enters the literal { key
{backspace}
Deletes character from right to the left of the cursor
{del}
Removes character from left to the right of the cursor
{downarrow}
Shifts cursor down
{end}
Shifts cursor to the end of the line
{enter}
Types the Enter key
{esc}
Types the Escape key
{home}
Shifts cursor to the start of the line
{insert}
Positions character to the right of the cursor
{leftarrow}
Moves cursor left
{movetoend}
Shifts the cursor to end of typeable element
{movetostart}
Shifts the cursor to the start of typeable element
{pagedown}
Scrolls down
{pageup}
Scrolls up
{rightarrow}
Shifts cursor right
{selectall}
Selects all the text by creating a selection range
{uparrow}
Shifts cursor up
Options
We can pass in the objects as options to modify the default behaviour of .type()
Parse special characters for strings surrounded by {}, such as {esc}. You can set the option to false to enter the literal characters.
release
true
This option allows to enable a modifier stay activated between commands
scrollBehavior
scrollBehavior
Viewport position to where an element to be scrolled before executing any command
timeout
defaultCommandTimeout
Time to wait for .type() command to resolve before time out
waitForAnimations
waitForAnimations
To say whether to wait for elements to finish animating before executing any command.
Options for type command
Example
Let us see examples for .type() command
cy.get('textarea').type('Hey there') // enter value in the text area
cy.get('body').type('{shift}') //enables the shift key
cy.get('body').type('{rightarrow}') //type event right arrow
Cypress Clear Command
Clear command will clear the values in input area or the text field.
Syntax
The syntax for clear command is a follows.
.clear()
.clear(options)
Options
We will look into the options that can be passed to the .clear() command.
Option
Default
Description
force
false
This will forces the action and disables waiting for actionability to occur
log
true
Shows the command in the Command log
scrollBehavior
scrollBehavior
Viewport position to where an element must be scrolled to before performing the command
timeout
defaultCommandTimeout
This option is the time to wait for .clear() to resolve before time out
waitForAnimations
waitForAnimations
This will wait for elements to complete animating before executing the command.
Options for clear command
Example
Let us look into the examples for clear command
cy.get('[type="text"]').clear() // Clear input of type text
cy.get('textarea').type('Welcome!').clear() // Clear textarea
cy.focused().clear() // Clear focused input/textarea
Cypress Check Command
The check command will check or in simpler words, tick the checkboxes or radio buttons. You can uncheck the checkboxes or radio buttons by using the .uncheck() command.
Syntax
We will understand the syntax for check command in Cypress.
//Syntax for check command
.check()
.check(value)
.check(options)
.check(values, options)
//Syntax for uncheck command
.uncheck()
.uncheck(value)
.uncheck(options)
.uncheck(values, options)
Options
The possible options that can be passed to check/uncheck commands are the options same as the clear command listed above
Example
We will look into the example of how we can use check and uncheck commands.
cy.get('[type="checkbox"]').check() // Check checkbox element
cy.get('[type="radio"]').first().check() // Check first radio element
cy.get('[type="radio"]').check('Male') //Check the radio element which has Male
cy.get('[type="checkbox"]').uncheck() //Uncheck checkbox element
cy.get('[type="radio"]').uncheck() //Uncheck the first radio element
cy.get('[type="checkbox"]').uncheck('Breakfast') // Uncheck the breakfast element
Cypress Select Command
The select Cypress command allows you to select elements within a <select> tag.
This option is the time to wait for .select() to resolve before time out
Options for select command
Example
Let us look into examples for the select command
cy.get('select').select('butterfly') // Select the 'butterfly' option
cy.get('select').select(0) // selects the element with 0 index
cy.get('select').select(['parrot', 'peacock']) //selects the parrot and peacock option
Cypress Trigger Command
Trigger command helps to trigger any event on the element.
Syntax
We will look into the syntax for accessing the trigger command
Trigger command accepts all the options that are mentioned for .clear() command. Additionally, there are few options we can configure that are listed below.
Option
Default
Description
bubbles
true
Whether the event should bubble
cancelable
true
Whether the event can be cancelled
eventConstructor
Event
The constructor for creating the event object (e.g. MouseEvent, keyboardEvent)
Option for Trigger command
Example
Let us different ways of using .trigger() in the code.
cy.get('a').trigger('mouseover') // Trigger mouseover event on a link
cy.get('.target').trigger('mousedown', { button: 0 }) //mousedown triggered at button 0
cy.get('button').trigger('mouseup', topRight, { bubbles: false }) //mouseup triggered on topRight position with setting bubble as false
Are Cypress commands async?
All the Cypress commands are asynchronous. They are queued for execution at a later point in time and will not wait for the completion of the commands. Cypress command do not do anything at the time of their invoke,instead they save it for later for execution. You can understand the asynchronous behaviour of Cypress here
Cypress Chainable Commands
In Cypress, we can use a series of commands to interact with elements in DOM. It is imperative to understand how the chaining of commands work internally. If we are chaining commands in a particular line, then Cypress will handle a promise based on the command chain and will yield a command based on the subject to the next command, until the chain of commands end or an error has occurred.
Cypress allows us to click an element or type into elements using the .click() or .type() commands by getting the elements using cy.get() or cy.contains(). Let us see a simple example of chaining commands
cy.get('textarea').type('How are you?')
In the above example, cy.get() is one Cypress command and .type() is another command, where we are chaining the .type() command onto the cy.get() command, telling it to type to the subject that is yielded from the cy.get() element. Similarly, we can chain all the commands that we discussed above.
Chaining Assertion Commands in Cypress
Similar to chaining multiple commands using Cypress, we can also chain assertions with commands. Assertions are commands that let you to describe the expected state or behaviour of the application. Cypress will wait until the elements reach the expected state, and the test will fail if the assertions don’t pass. We will see how we can use chaining commands in asserting an element.
cy.get('button').should('be.disabled') //expect whether the button should be disabled
cy.get('form').should('have.class', 'form-vertical') //expect whether the form should have class as 'form-vertical'
cy.get('input').should('not.have.value', 'Name') // assert whether the input should not have the value 'Name'
As listed above, we are using the cy.get() command and chaining it with the .should() assertion command to expect the behaviour based on the result. This way, we can use chain assertion commands in Cypress.
Cypress Custom Commands
Cypress provides us API’s to create commands based on our requirements. Cypress custom command is similar to the default commands that are pre-existing, except it is user-defined. With custom commands, we can play around with the commands and chain them based on our use case. Cypress custom commands are useful in our workflow if you require reusing them over and over in the tests.
Let us see the syntax for creating a new custom command in Cypress.
name – The name of the command in string that we want to add or overwrite
callbackFn – This function takes an argument passed to the command
options – Pass any options object to define the behaviour of the command
Note : options are supported only for the add commands and do not support for the overwrite commands
Option
Accepts
Default
Description
prevSubject
Boolean, String or Array
false
defines how to handle the previously yielded subject.
The options that prevSubject accepts are as follows
false – ignore previous subjects (parent command)
true – accept the previous subjects (child command)
optional – pass in whether you want to start a new chain or use an existing chain (dual command)
Parent Custom Command in Cypress
We will see how to add a parent custom command in Cypress. Parent command will always begin a new chain of commands, eventhough you have chained off a previous command. The previously chained command will be ignored and a new command will be chained always. Some of the parent commands are cy.visit(), cy.get(), cy.request(),cy.exec(), cy.route()
Example
We will see an example of how to write a parent custom command in Cypress
Cypress.Commands.add('clickLink', (label) => {
cy.get('a').contains(label).click()
})
//clicking the "Buy Now" link
cy.clickLink('Buy Now')
In the above example, ‘clickLink‘ is the name of our custom command. It will search for the label. In line 2, the command gets ‘a‘, and search for the link which contains label and click the element. cy.clickLink() will execute the action in the test file and clicks the “Buy Now” link.
Child Custom Command in Cypress
Child Custom commands in Cypress are chained off a parent command or another child command. The subject from the previous command will be yielded to the callback function.
Some of the Cypress commands that can be chained as a child command are .click(), .trigger(), .should(), .find(), .as()
Example
We will look into an example on how to chain a child custom command
Cypress.Commands.add('forceClick', {prevSubject: 'element'}, (subject, options) => {
// wrap the existing subject and do something with it
cy.wrap(subject).click({force:true})
})
//accessing the forceClick in the test file
cy.get("[data-test='panel-VALUES']").forceClick();
In the above example, we are naming our custom command as ‘forceClick‘. We are passing the prevSubject argument to the element and wrapping the existing subject. With cy.wrap(), we are force clicking the subject. Then in the test file, we are accessing the custom command, ‘forceClick‘ on a cy.get() command.
Dual Custom Commands in Cypress
Dual custom commands are hybrid between a parent and child command. You can begin a new chain of commands or chain off an existing command. Dual commands are helpful if we want our command to work in different ways with or without the existing subject.
Some of the commands that can be used for dual commands are cy.contains(), cy.screenshot(), cy.scrollTo(), cy.wait()
Example
Let us see an example of how to use dual custom commands
In some cases, we will require to get the button of the text using getButton which will acquire all the button of the element. Now we can use getButton to chain the with the parent element or chain the child element, where it can invoke the elements of the parent.
Since the prevSubject is optional, we can either pass the subject as an argument or invoke the command without the subject in the test file as below
cy.getButton() // without the subject
cy.get('#loginBtn').getButton() // with the subject
Overwriting Existing Cypress Commands
We can overwrite the already existing Cypress commands and modify the behaviour in order to avoid creating another command that will try to use the original command at the end.
Some of the original Cypress command that can be overwritten are cy.visit(), cy.type(), cy.screenshot(), cy.contains()
Cypress.Commands.overwrite('contains',
(originalFn, subject, filter, text, options = {}) => {
// determine if a filter argument was passed
if (typeof text === 'object') {
options = text
text = filter
filter = undefined
}
options.matchCase = false
return originalFn(subject, filter, text, options)
}
)
As we saw above, we are using the Cypress.Commands.overwrite to modify the existing Cypress command. We are naming our custom command as contains and we are passing arguments to determine whether the filter argument has passed.
Cypress Import Commands
In this section, we will understand how to import Cypress Commands.
We must create our Cypress custom commands in the cypress/support/commands.js file. We shall add the custom commands in the command.js file and import in our test case file to use it.
Command.js file
Cypress Custom Commands with Example
We will understand how to create a custom command and use it in our spec file with real-time example.
As we saw above, we have to add new custom commands under the commands.js file. In that file, let us add a custom command for a login function
Cypress.Commands.add("login", (username, password) => {
//adding a new command named login
cy.get('[id=Email]').clear();
cy.get('[id=Email]').type(username);
cy.get('[id=Password]').clear();
cy.get('[id=Password]').type(password);
cy.get('[type=submit]').click();
});
Custom commands in command.js file
In the above code, we are naming our custom command as login. Inside the custom command, we have added the steps of clearing the username field and entering value in the textfield. Similarly, we are clearing the field and adding the password in the password field. Later, we are clicking the submit button. This is a simple custom command that accepts two arguments : username and password. We will be passing the value for the username and password in our spec file.
Now let us create a spec file named customCommand.spec.js under integration folder. Our spec file will contain the following code
As we saw above, we are accessing our custom command as cy.login() ,where we are passing the values of username and password.
Cypress Custom Commands IntelliSense
IntelliSense provides intelligent code suggestions in the IDE or code editor directly while we are writing tests. It helps by showing a popup that displays the command definition, link to the documentation page and code examples. If we are using any modern code editor like Visual Studio Code or IntellJ, then IntelliSense will be very useful.
IntelliSense uses Typescript to understand and displays the syntax. If we write custom commands and provide TypeScript definitions for the custom commands, we can use the triple slashes to display IntelliSense, even if our project uses JavaScript only.
To configure IntelliSense, we need to describe the code in cypress/support/index.d.ts file.
Now, we should let our spec files know that there are some Typescript definitions in the index.d.ts file. So, at the beginning of our spec file, add the below code to let IntelliSense provide suggestions for us.
// type definitions for custom commands like "login"
// will resolve to "cypress/support/index.d.ts"
// <reference types="../support" />
Suggestion provided by IntelliSense
As we saw above, IntelliSense provides us with the arguement we provided in our command.js file and helps in auto-completing.
Page Object Model, commonly known as POM, is a popular pattern in any automation framework. Page Object Model can be applied in Cypress too. Page Object Model has many advantages in creating a framework for test automation, such as reducing code duplication and increasing maintainability and readability. Cypress provides us the flexibility to incorporate Page Object Model in the test script. In this article, we will look at creating a Page Object Model in Cypress step by step with examples.
Page Object Model is a design pattern where the page objects are separated from the automation test scripts. Automation testing gives us many leverages that benefit us in testing; however, there are some outcomes such as code duplication and an increase in the risk of maintainability as the project grows. Let us understand the significance of POM with an example.
Consider we have multiple pages in our application like Login Page, Registration Page, and Book Flights page.
The Login page contains all the web elements of the login functionalities
The Registration contains all the methods and web elements of the registration process
The Book flights contain the web elements of the flight booking page
There are three test cases, namely TC1, TC2, and TC3.
TC1 contains the login test cases.
TC2 contains login and registration test cases
TC3 contains login, registration, and flight booking test cases
Example without POM
Now, the login page interacts with TC1.
Registration page needs to interact with TC1 and TC2, and
The flight booking page needs to interact with TC1, TC2, and TC3
As you can see, there are common functionalities between all three test cases. Instead of writing the methods and locators of login in all the test case files, we can have them separately and access them across the files. This way, the code is not repeated, and it is easily readable.
One of the best practices in coding is a concept called DRY. It means Do Not Repeat Yourself. As the full form clearly says, we should not repeat the lines of code again and again. To overcome this, Page Object Model plays an important role in best coding practices.
Page Object Model Framework Architecture
The page object model framework architecture is a proven architecture that can customize with simple methods. Today, almost all companies follow agile methodologies, which involve continuous integration, development, and testing. The automation testers maintain the test framework to work alongside the development process with the Page Object Model. It is a significant design pattern in maintaining the automation test framework as the code grows with new features.
The page object is a design pattern that is an object-oriented class that interacts with the pages of the application we are testing. Page Object comprises of Page Class and Test cases. Page class consists of methods and locators to interact with the web elements. We create separate classes for every page in the application. We will be creating individual methods for each functionality and access them in our spec file.
Page Object Model
Advantages of using Page Object Model in Cypress
The methods are reusable across the whole project and easy to maintain when the project grows. The lines of code become lessreadable and optimized.
Page Object Pattern suggests that we separate the operations and flow that we are performing in the UI from verification steps. When we follow the POM pattern, we tend to write clean and easily understandable code.
With the Page Object Model, objects and test cases are independent of each other. We can call the objects anywhere across the project. This way, if we are using different tools like TestNG/JUnit for functional testing or Cucumber for acceptance testing, then it is easily accessible.
Step By Step Page Object Model Cypress with Example
This section will understand how to create a Page Object Model in Cypress with real-time examples that we can implement in projects. We will understand from the basic setup and step-by-step process for creating a Page Object Model.
Let’s discuss the scenario on which we will write the functions in this example.
Navigate to https://admin-demo.nopcommerce.com/ website
Enter valid username and password
Click on the Login Button
Validate the URL whether it is appended with /admin after login
We will be creating two files – one PageObject file and one spec file for this example. Let us begin!
Step 1: Open our project in VS code. Create a folder called PageObject under the integration folder. Under this folder, you can create page object files for any modules.
New folder named PageObject
Step 2: Create a file named LoginPage.js under the PageObject folder. In LoginPage.js, we will be writing the methods that involve the login functionalities.
LoginPage.js creation under PageObject folder
Step 3: Let’s start writing our first test method in the LoginPage.js file. We have to first create a class that we will be exporting in our spec file. We will call our class as LoginPage
class LoginPage {
}
Based on our pseudocode, our first step is to navigate to the URL. We will call our method as navigate(). Inside our navigate method, we shall add the cy.visit() function from Cypress.
Step 4: Now, we will have to enter the username in our email field. We will name our method as enterEmail(). First, we should get the locator of the email field and access them via cy.get() command. Then we will clear the field using the clear() command and add the username using the type() command. In our method, we pass a parameter username to pass the value in the spec file. This way, we are keeping it generic to access this method if a different email id is required.
enterEmail(username) {
cy.get('[id=Email]').clear()
cy.get('[id=Email]').type(username);
return this
}
Instead of writing the cy.get() command twice in the above code, we can simply loop them with the dot operator.
enterEmail(username) {
cy.get('[id=Email]')
.clear()
.type(username);
return this
}
enterEmail method
You might have noticed return this in line 9. this indicates that the enterEmail method belongs to the particular LoginPage class. Basically, this represents the class.
Step 5: We have to create a method for passwords similar to our enterEmail method. We will call our password method as enterPassword(). Initially, we will get the locator for the password, clear the field and type the input value. We will pass a parameter to our method called pswd and access in the type() command.
enterPassword(pswd) {
cy.get('[id=Password]')
.clear()
.type(pswd)
return this
}
enterPassword method
Step 6: Our last method would be to click on the login button. We shall name our method as submit(). We will get the locator and click the button using the click() method from Cypress.
submit() {
cy.get('[type=submit]').click()
}
submit method
Step7: Now, we have to export this class to use it across our spec file. For this, we just add one line outside our class, and we can easily access it in our spec file.
export default LoginPage
export command
Hurray! We have created a Page Object file for our project. It was pretty simple and easy!
Accessing the Page Objects in the Spec file
Now let us move on to our test case file. We have to create a spec file in our integration folder. We shall call our spec file POMDemo.spec.js.
POMDemo.spec.js file creation
Step 1: To access our methods in the LoginPage.js file, we must import them into our spec file. We import by using the import statement. We should navigate to the LoginPage.js file by using ../
In our case, the path is ../integration/PageObject/LoginPage. So, the import statement will look something like the below.
import LoginPage from "../integration/PageObject/LoginPage"
Step 2: Since we use Mocha, we will write our test case inside describe() and it()block. describe() represents a test suite, and it() represents a test case. Both the blocks are a function and accept a string parameter that includes the description of the test.
describe("Cypress POM Test Suite", function () {
})
Describe block
Inside the describe block, we will write our it() by adding the description as login with valid credentials.
it("Login with valid credentials", function () {
})
it block
Step 3: To access our methods from our Page object file, we should create an instance for our Login class. To create an instance for the login class, we must declare a variable and assign it to our class file using the new keyword. With the declared variable, we can easily access the methods from the Page object file.
const login = new LoginPage();
Instance of a class
Note: With the variable login, we can access the methods from the Page object class. When we start typing login. , the vscode will list the suggestions of all the methods available in the LoginPage.js file. This helps us to verify that we have exported and imported our class properly!
Step 4: Let us call our navigate() method to visit the URL. This is the first action in our test case.
/// <reference types="cypress" />
import LoginPage from "./PageObject/LoginPage"
describe("Cypress POM Test Suite", function () {
it("Login with valid credentials", function () {
const login = new LoginPage();
login.navigate();
});
});
Step 5: We should enter the username in the email field. We access the enterEmail() with the login object. enterEmail() method accepts a parameter username. So we should pass the value for the username as a string in our spec file
/// <reference types="cypress" />
import LoginPage from "./PageObject/LoginPage"
describe("Cypress POM Test Suite", function () {
it("Login with valid credentials", function () {
const login = new LoginPage();
login.navigate();
login.enterEmail('admin@yourstore.com');
})
})
Step 6: Similar to step 5, we should call our enterPassword() method by passing the password as a parameter in the string.
/// <reference types="cypress" />
import LoginPage from "./PageObject/LoginPage"
describe("Cypress POM Test Suite", function () {
it("Login with valid credentials", function () {
const login = new LoginPage();
login.navigate();
login.enterEmail('admin@yourstore.com');
login.enterPassword('admin');
})
})
Step 7: Next, we have to click on the login button. We will call the method submit() from our page object file.
/// <reference types="cypress" />
import LoginPage from "./PageObject/LoginPage"
describe("Cypress POM Test Suite", function () {
it("Login with valid credentials", function () {
const login = new LoginPage();
login.navigate();
login.enterEmail('admin@yourstore.com');
login.enterPassword('admin');
login.submit();
})
})
Step 8: After logging in, we have to assert the URL. We will verify whether the URL is equal to the URL after login. For assertion, we will use the Chai assertion library, which is inbuilt with Cypress.
/// <reference types="cypress" />
import LoginPage from "./PageObject/LoginPage"
describe("Cypress POM Test Suite", function () {
it("Login with valid credentials", function () {
const login = new LoginPage();
login.navigate();
login.enterEmail('admin@yourstore.com');
login.enterPassword('admin');
login.submit();
cy.url().should('be.equal', 'https://admin-demo.nopcommerce.com/admin/')
})
})
Login test case
The above image represents the login test case. We were able to write a test case with a Page Object Model with very few simple steps. Now let us run the test case and see the result.
We shall open the Cypress test runner and click on the spec file and run our test case. Check this article on how to open Cypress test runner.
Test Result in Cypress
Hurray! We have successfully written a test case that uses Page Object Model in Cypress. We can incorporate this pattern in real-time projects. There are many ways that we can write the methods in a page object file. I have shown you an example that is standard and works for any project. You can also write only the return function in the page object file and then click and type directly in our spec file.
We will see another pattern that we can use in the project. This method will also work perfectly fine.
In this type, we will be returning only the locator function in our method and perform actions in the test file. We will write code for the same scenario we saw above.
As we saw above, we are writing only the locator inside our function and returning them. The return represents that the particular method belongs to the class LoginPage.js. We are not adding any actions in our methods.
Page Object File example
Spec File – POMDemo.spec.js
We will look into the example of accessing the methods in the spec file.
/// <reference types="cypress" />
import LoginPage from "./PageObject/LoginPage"
describe("Cypress POM Test Suite", function () {
it("Login with valid credentials", function () {
const login = new LoginPage();
login.navigate();
login.enterEmail().clear()
login.enterEmail().type('admin@yourstore.com');
login.enterPassword().clear()
login.enterPassword().type('admin');
login.submit().click();
cy.url().should('be.equal', 'https://admin-demo.nopcommerce.com/admin/')
});
});
Spec file Example
Here, we call the method from the PageObject file and perform the test case actions. So first, we are calling our reference variable login and then appending it with the method enterEmail() and finally appending the action type. In our type(), we are passing the username value.
Test Result
As you can see, all the commands have been executed, and the test case has passed!
You can choose whichever Page Object Model suits your project and your opinion. There is no particular rule to stick to only one procedure.
How to use Fixtures as a Test Data Source in Page Object Model in Cypress?
In our Page Object Model examples, we passed the username and password value directly in either the Page Object file or directly in the test case file. This section will understand how to use fixtures in Cypress to keep the data safe and not exposed. We should try to keep all the credentials and data in one file and access them. This way, it is easy to maintain, and sensitive data like username and password are not exposed. This method is also one of the procedures that we need to follow in Page Object Pattern.
As discussed earlier, Fixture helps store data in a JSON file or excel file, or an external library like Apache POI. We will use these data by creating a variable and access them in our spec file. Let us understand with an example.
Cypress provides a folder called “fixtures.” We will create a JSON file called credentials.json under the ‘Fixtures’ folder.
JSON file creation
Let us declare our username, password, and URL values that we need to validate in a JSON format in the credentials.json file.
Accessing the values from the JSON file in the test case file
As we have defined the values in our JSON file, we will access them in our test case file using Fixtures from Cypress. We will access the JSON value with this keyword. Let’s wrap the fixture function in a before() block.
describe("Cypress POM Test Suite", function () {
before(function () {
cy.fixture('credentials').then(function (testdata) {
this.testdata = testdata
})
})
cy.fixture(‘credentials’).then(function (testdata) { this.testdata = testdata }) – this line represents that we are passing the credentials.json file as a parameter to our cy.fixture() command. Here, we are not required to pass whether it is a JSON file. Just pass the file name alone. Later, we pass testdata as a parameter in the function and access the testdata variable using this.
/// <reference types="cypress" />
import LoginPage from "./PageObject/LoginPage"
describe("Cypress POM Test Suite", function () {
before(function () {
cy.fixture('credentials').then(function (testdata) {
this.testdata = testdata
})
})
it("Login with valid credentials", function () {
const login = new LoginPage();
login.navigate();
login.enterEmail(this.testdata.username)
login.enterPassword(this.testdata.password)
login.submit();
cy.url().should('be.equal', this.testdata.adminUrl)
});
});
login.enterEmail(this.testdata.username) – This will fetch the username value from the credentials.json file and fill it into the email field.
login.enterPassword(this.testdata.password) – This will fetch the password value from the credentials.json file and fill it into the password field
cy.url().should(‘be.equal’, this.testdata.adminUrl) – This will get the adminUrl from the credentials.json file and validate in the assertion
Passing the data from JSON file to spec file
Now, let us run the test case for the result.
Test result
As we can see, the test cases have been executed and have passed. This example will help you to write a basic Data-driven test case. You can incorporate it in your project using this method. You can create new JSON files under the Fixture folder, add values related to test data, and access it across any test file.
Frequently Asked Questions
Does Cypress support Page Object Model?
Of course. Cypress gives all the flexibility to play around with pages and objects in the repository. It is easy to implement.
Which Page Object Model should I use from the above examples?
There is no particular rule to stick to only one way of Page Object Model. You can use any model that has been discussed above. You are free to customize the model according to your project.
Why should I use fixtures in the Page Object Model in Cypress?
Fixture helps store sensitive data like username, password, and URLs in a separate file like JSON or excel. This ensures the application’s security and access them easily in any files across the project. To access the JSON file, we use fixtures to use it in our spec file.
In our previous article, we saw the configurations in Cypress and various options that can be configured in JSON files. This article will understand Cypress Promise and Cypress Asynchronous behaviour with hands-on implementation and examples in our project. We will also discuss how to incorporate awaits in our asynchronous code and some essential functions like wrap() and task(). Let’s get started!
Cypress Promise
Cypress Promise and Cypress Asynchronous:
Cypress Promise and Cypress Asynchronous nature are some of the essential concepts. Like any other Javascript framework, Cypress also revolves around Asynchronous and Promises. Cypress handles all the asynchronous behaviour internally, and it’s hidden from the user. We will use .then() to handle promises manually in our code. There are external packages like Cypress-promise in npm where we can manipulate the Cypress asynchronous behaviour. We will discuss each of these topics in detail.
Table of Contents
Cypress Asynchronous
As we know, Cypress is based on Node JS. Any framework that is written build from Node.js is asynchronous. Before understanding the asynchronous behavior of Cypress, we should know the difference between synchronous and asynchronous nature.
Synchronous nature
In a synchronous program, during an execution of a code, only if the first line is executed successfully, the second line will get executed. It waits until the first line gets executed. It runs sequentially.
Asynchronous nature
The code executes simultaneously, waits for each step to get executed without bothering the previous command’s state. Though we have sequentially written our code, asynchronous code gets executed without waiting for any step to complete and is completely independent of the previous command/code.
What is asynchronous in Cypress?
All Cypress commands are asynchronous in nature. Cypress has a wrapper that understands the sequential code we write, enqueues them in the wrapper, and runs later when we execute the code. So, Cypress does all our work that is related to async nature and promises!
Let’s understand an example for it.
it('click on the technology option to navigate to the technology URL', function () {
cy.visit('https://lambdageeks.com/') // No command is executed
//click on the technology option
cy.get('.fl-node-5f05604c3188e > .fl-col-content > .fl-module > .fl-module-content > .fl-photo > .fl-photo-content > a > .fl-photo-img') // Nothing is executed here too
.click() // Nothing happens yet
cy.url() // No commands executed here too
.should('include', '/technology') // No, nothing.
});
// Now, all the test functions have completed executing
// Cypress had queued all the commands, and now they will run in sequence
That was pretty simple and fun. We now understood how Cypress Asynchronous commands work. Let us dive deeper into where we are trying to mix sync and async code.
Mixing Cypress Synchronous and Asynchronous commands
As we saw, Cypress commands are asynchronous. When injecting any synchronous code, Cypress does not wait for the sync code to get executed; hence the sync commands execute first even without waiting for any previous Cypress commands. Let us look into a short example to understand better.
it('click on the technology option to navigate to the technology URL', function () {
cy.visit('https://lambdageeks.com/')
//click on the technology option
cy.get('.fl-node-5f05604c3188e > .fl-col-content > .fl-module > .fl-module-content > .fl-photo > .fl-photo-content > a > .fl-photo-img')
.click()
cy.url() // No commands executed here too
.should('include', '/technology') // No, nothing.
console.log("This is to check the log") // Log to check the async behaviour
});
});
Synchronous execution of the log command
The log is added at the end of the code, which is a sync command. When we run the test, you can see that the log has been printed even before the page is loaded. This way, Cypress does not wait for the synchronous command and executes it even before executing its commands.
If we want them to execute as expected, then we should wrap it inside the .then() function. Let us understand with an example.
it('click on the technology option to navigate to the technology URL', function () {
cy.visit('https://lambdageeks.com/')
//click on the technology option
cy.get('.fl-node-5f05604c3188e > .fl-col-content > .fl-module > .fl-module-content > .fl-photo > .fl-photo-content > a > .fl-photo-img')
.click()
cy.url() // No commands executed here too
.should('include', '/technology') // No, nothing.
.then(() => {
console.log("This is to check the log") // Log to check the async behaviour
});
});
Async execution with .then() command
What is Cypress Promise?
As we saw above, Cypress enqueues all the commands before execution. To rephrase in detail, we can say that Cypress adds promises(commands) into a chain of promises. Cypress sums all the commands as a promise in a chain.
To understand Promises, compare them with a real-life scenario. The explanation defines the Promise in asynchronous nature too. If someone promises you, they either reject or fulfill the statement they made. Likewise, in asynchronous, promises either reject or fulfill the code we wrap in a promise.
However, Cypress takes care of all the promises, and it is unnecessary to manipulate them with our custom code. As a Javascript programmers, we get curious about using awaits in our commands. Cypress APIs are completely different than we are used to generally. We will look into this a later part of this tutorial in depth.
States of Cypress Promises
Promises have three different states based on the Cypress commands. They are
Resolved – Occurs when the step/ command gets successfully executed.
Pending – State where the execution has begun, but the result is uncertain.
Rejection – Occurs when the step has failed.
As a Javascript programmer, we tend to write promises in our code and return them. For example,
//This code is only for demonstration
describe('Cypress Example ', function () {
it('click on the technology option to navigate to the technology URL', function () {
cy.visit('https://lambdageeks.com/')
//click on the technology option
cy.get('.fl-node-5f05604c3188e > .fl-col-content > .fl-module > .fl-module-content > .fl-photo > .fl-photo-content > a > .fl-photo-img')
.then(() => {
return cy.click();
})
cy.url()
.then(() => {
return cy.should('include', '/technology')
})
});
});
Here, we are returning promises to each of the commands. This is not required in Cypress. Fortunately, Cypress takes care of all the promises internally,and we don’t need to add promises in each step. Cypress has the retry-ability option, where it retries for a particular amount of time for executing the command. We will see an example of a code without including promises manually.
it('click on the technology option to navigate to the technology URL', function () {
cy.visit('https://lambdageeks.com/')
//click on the technology option
cy.get('.fl-node-5f05604c3188e > .fl-col-content > .fl-module > .fl-module-content > .fl-photo > .fl-photo-content > a > .fl-photo-img')
.click()
cy.url()
.should('include', '/technology')
});
});
Cypress commands with promises handled internally
The above code is not clumsy and is easy to read and understand. Cypress handles all the promise work, and it is hidden from the user. So we don’t have to worry about handling or returning the promises anywhere!
How do you use await in Cypress?
As discussed above, Cypress has its way of handling asynchronous code by creating a command queue and running them in sequence. Adding awaits to the commands will not work as expected. Since Cypress is handling everything internally, I would recommend not adding awaits to the code.
If you need to add awaits, you can use a third-party library like Cypress-promise that changes how Cypress works. This library will let you use promises in the commands, and use await in the code
Let us understand the ways to use awaits and how not to use them.
You should not use awaits like this
//Do not use await this way
describe('Visit the page', () => {
(async () => {
cy.visit('https://lambdageeks.com/')
await cy.url().should('include', '/technology');
})()
})
In the example, we are wrapping the getName and then invoke the name for it.
Cypress Wrap Promise
We can wrap the promises that are returned by the code. Commands will wait for the promise to resolve before accessing the yielded value and. then proceed for the next command or assertion.
const customPromise = new Promise((resolve, reject) => {
// we use setTimeout() function to access async code.
setTimeout(() => {
resolve({
type: 'success',
message: 'Apples and Oranges',
})
}, 2500)
})
it('should wait for promises to resolve', () => {
cy.wrap(customPromise).its('message').should('eq', 'Apples and Oranges')
});
When the argument in cy.wrap() is a promise, it will wait for the promise to resolve. If the promise is rejected, then the test will fail.
Cypress-promise npm
If we want to manipulate the promises of Cypress, then we can additionally use a library or package called Cypress-promise and incorporate it in our code. This package will allow you to convert a Cypress command into a promise and allows you to await or async in the code. However, these conditions will not work before or beforeEach the blocks. Initially, we should install the package in our project by passing the following command in the terminal.
npm i cypress-promise
Once installed, the terminal will look something like this.
Cypress-promise install
After installation, we should import the library into our test file.
import promisify from 'cypress-promise'
With this library, you can create and override the native Cypress promise and use awaits and async in the code. You should access the promise with the promisify keyword. Let us look into an example for the same.
import promisify from 'cypress-promise'
it('should run tests with async/await', async () => {
const apple = await promisify(cy.wrap('apple'))
const oranges = await promisify(cy.wrap('oranges'))
expect(apple).to.equal('apple')
expect(oranges).to.equal('oranges')
});
Promisify in Cypress-promise
This was very simple and fun to learn! This way, you can assign asynchronous code in Cypress.
Cypress Async Task
task() is a function in Cypress that runs the code in Node. This command allows you to switch from browser to node and execute commands in the node before returning the result to the code.
task() returns either a value or promise. task() will fail if the promise is returned as undefined. This way, it helps the user capture typos where the event is not handled in some scenarios. If you do not require to return any value, then pass null value.
Frequently Asked Questions
Is Cypress Synchronous or Asynchronous?
Cypress is Asynchronous by returning the queued commands instead of waiting for the completion of execution of the commands. Though it is asynchronous, it still runs all the test steps sequentially. Cypress Engine handles all this behavior.
Is it possible to catch the promise chain in Cypress?
Cypress is designed in a way that we will not be able to catch the promises. These commands are not exactly Promises, but it looks like a promise. This way, we cannot add explicit handlers like catch.
We will discuss the JSON structure, examples, and detailed hands-on experience to write JSON in our code. But, first, let’s dive into our article!
What is Cypress Json: Example, Schema, Detailed Hands-On Analysis
In our previous article, we discussed variables and aliases and how to write our first test case. Now, we will discuss Cypress JSON and how to incorporate it into our code.
As we saw earlier, the first time we open our Cypress Test Runner, it creates a cypress.json file. This file is used to pass any configuration values we require. So first, we will look into the options that we can pass in our cypress.json file.
Default JSON Options
Certain options are set by default in Cypress. However, we can customize them according to our project. To identify the default values set by Cypress, navigate to the Settings folder in our Cypress Test Runner. From there, expand the Configuration option to view the default options set by Cypress.
Cypress JSON File
The options are the default configurations provided by Cypress.
We can change the default options of Cypress by passing any arguments that are compatible with our project. As the name suggests, cypress.json is a JSON file, so we have to pass our arguments in JSON format. In our VS code, you could see that the cypress.json is empty with no arguments passed to it. Now let us see the different options that we can pass in our JSON file.
Global Options
We can pass the global options to arguments that need to be accessed globally. For example, in the table below, the Options column represents the keyword we will be passing in our JSON file; Default indicates the default value of the particular option set by Cypress, and Description indicates the meaning of the option.
Option
Default
Description
baseUrl
null
We can set the URL globally instead of passing in each file. It can be used for cy.visit() or cy.request() commands
clientCertificates
[]
You can use this option for configuring client certificates on a URL basis
env
{}
You can pass any environment variables as a value. This option will be useful if we are testing our application in different environments like staging or production.
watchForFileChanges
true
This option checks whether Cypress watches and restarts tests on any file changes are made.
port
null
We can pass the port number on hosting Cypress. A random port is generated, but we can add the port number we require.
numTestsKeptInMemory
50
This option is the number of test snapshots and commands data that are stored in memory. If there is high memory consumption in the browser during a test run, we can reduce the number.
retries
{ "runMode": 0, "openMode": 0 }
This option is to specify the number of times to retry a test that is failing. We can configure it separately for cypress run and cypress open.
redirectionLimit
20
We can configure the limit for the number of times the application can be redirected before an error occurs.
includeShadowDom
false
The ability to navigate inside the Shadow DOM to interact with elements. By default, it is set to false. If our application has any element requiring shadow root navigation, you can set it to true.
Cypress JSON Timeout
Timeout is one of the most important concepts in any automation framework. Cypress provides a variety of options that helps in handling timeouts in our scripts. First, we will look into the options that we can configure.
Option
Default
Description
defaultCommandTimeout
4000
This option is to wait for the DOM Elements-based commands to load. This is in milliseconds.
requestTimeout
5000
Time, in milliseconds, to wait until the request of cy.wait() command to go timeout.
responseTimeout
30000
This timeout is to wait until a response in a series of commands such as cy.request(), cy.wait(), cy.fixture(), cy.getCookie(), cy.getCookies(), cy.setCookie(), cy.clearCookie(), cy.clearCookies(), and cy.screenshot() commands
taskTimeout
60000
Timeout, in milliseconds, for the completion for the execution of cy.task() command
execTimeout
60000
This time in milliseconds is to wait to finish execution of the cy.exec() command, which is the completion of the system command
pageLoadTimeout
60000
This timeout waits for page navigation events or commands that interact with the pages like cy.visit(), cy.go(), cy.reload()
Cypress Read JSON File
Sometimes, we will require to interact with the folders or files in our project. To interact, we have to set certain options in our cypress.json file to manipulate the files. So, first, let us look into the options available in our folders/ files configuration.
Option
Default
Description
downloadsFolder
cypress/downloads
This is the path where the files are downloaded and stored during a test run
fixturesFolder
cypress/fixtures
This is the path to the folder that contains the fixture files. We can pass false to disable storing the files.
ignoreTestFiles*.hot-update.jsYou can pass this as a string or array of global patterns to ignore test files for the test run. However, it would be displayed in the test files.
integrationFolder
cypress/integration
Integration test files are stored in this path to the folder.
pluginsFile
cypress/plugins/index.js
This path is where the plugins are stored. You can pass the argument as false to disable this configuration.
screenshotsFoldercypress/screenshotsScreenshots from the execution of cy.screenshot() command and test failure during cypress run are stored in this foldersupportFilecypress/support/index.jsHere the test files that load before the test are stored. You have the option to disable by passing false
testFiles
**/*.*
Path to the test files that need to be loaded. It is either a string or array of global patterns.
videosFolder
cypress/videos
Folder path which will store videos during test execution
Screenshots and Video Options
We can configure our snapshots and videos in our cypress.json() file, and Cypress provides us some options to customize our configuration.
Option
Default
Description
screenshotOnRunFailure
true
Option to set to either true or false whether Cypress takes a screenshot during test failure when cypress runs. It is set to true by default
trashAssetsBeforeRuns
true
This option is to trash assets in the videosFolder, downloadsFolder and screenshotsFolder before every cypress run
videoCompression
32
This option is the quality of the video compression measured in the Constant Rate Factor(CRF). By passing false, you can also disable this option. You can pass values from 0 to 51, where the lowest value gives better quality.
videosFolder
cypress/videos
The folder where the video of the tests is saved.
video
true
Boolean value to capture the video of the test execution with cypress run.
videoUploadOnPasses
true
This option is to upload the videos to the Dashboard when all the test cases in a spec file are passing.
Viewport and Actionability
You can configure and pass values to change the viewport height and width with the options provided by Cypress. Actionability options can also be configured.
Option
Default
Description
viewportHeight
660
This is to provide the default height for the application in pixels. We can override this command with cy.viewport()
viewportWidth
1000
Option for the viewport width in pixels for the application. Can be overridden with cy.viewport() command.
animationDistanceThreshold
5
The threshold value for the distance measured in pixels where an element must exceed considering the time for animating.
waitForAnimations
true
Option to wait for the elements to complete the animation before performing any commands.
scrollBehavior
top
This is a viewport option that must scroll to an element just before performing any commands. Available options are 'center', 'top', 'bottom', 'nearest', or false, wherein false disables the scrolling.
Cypress JSON Example
Earlier, we saw the different configurations we can pass in our cypress.json file. Now, we will look into an example of how to use them in our project.
Overriding default values in the cypress.json file
In our VS code, open the cypress.json file. We will override the defaultCommandTimeout command to 8000.
{
"defaultCommandTimeout" : 8000
}
This is how it looks in our VS code project.
cypress.json file
By changing the cypress.json file, it applies to the whole framework. We can verify by navigating to our Cypress settings. It has changed from a default value of 4000 to 8000
Cypress settings default values
Overriding default values via the test script
We can manipulate the default values via our test script too. Instead of passing in the cypress.json file, we will pass it in our test file.
//Changing the timeout from 4 seconds to 8 seconds
Cypress.config('defaultCommandTimeout',8000)
// Test code
cy.get('#username').type(users.email)
cy.get('#pswd').type(users.password)
cy.get('#login_btn').click()
This way, we can override default values in our test file. However, this does not impact any configuration changes on the framework level. Cypress gives priority to the values in cypress.json. Lastly, it takes up the global configurations.
Cypress Fixture JSON Array
Cypress cy.fixture() is a function that loads a fixed set of data in a file. We can use the fixture as a JSON to load any values or array in the JSON file. First, let's understand how to access the JSON file in our project.
My JSON file has two properties: username and password. My JSON file name is examples.json.
In our spec file, we will access our fixture with the cy.fixture() command and the concept of aliases.
cy.fixture('example.json').as('example')
//Using the alias name to this keyword, So we can use globally
const userObj = this.userData
//looping our .json data with a new variable users
cy.get(userData).each((users) =>
{
//Write the test code.
cy.get('#username').type(users.email)
cy.get('#pswd').type(users.password)
}
Cypress env JSON
Environment variables are used across many projects in organizations. We use environment variables
when values are dynamic across different machines
when we want to test under different environments such as staging, testing, development, production/live
These cases require us to define environment variables. However, if we set an env variable in one spec file, it is not reflected across other spec files. This is because Cypress runs each spec files independently. This way, we will need to configure env variables separately.
We access our environment files from our Cypress JSON file, i.e., cypress.json file. So we will be required to assign the option in our cypress.json file and used it across our spec file. So let us dive into our example.
We can set our environment variables in our configuration file or cypress.env.json file.
Setting environment variable in cypress.json file
We set the env property by a key-value pair. Any values passed under the keyword env fall under environment variables, and Cypress takes the argument from the env keyword. The syntax looks like the below.
{
"env": {
"key1": "value1",
"key2": "value2"
}
}
If we want to access the env variable in our spec file, we assign them as mentioned below.
Cypress.env() //returns both the key1,value1 and key2, value2
Cypress.env(key1) //returns only the value1
We will add the env configuration in our project and will access them in our spec file. In our cypress.json file, add the following configuration. We are setting our URL property and assigning them to our URL. Here, URL is the key, and https://lambdageeks.com/technology/ is the value.
As we have declared the configuration, we will access them in our spec file. It looks something like below. As mentioned above, we will be using Cypress.env() method to access the env variable.
// type definitions for Cypress object "cy"
// <reference types="cypress" />
describe('Cypress Example ', function () {
it('accessing the environment variable', function () {
//Calling URL from cypress.json
cy.visit(Cypress.env('url'));
})
})
Setting environment variable in cypress.env.json file
We can assign our environment variable in our cypress env JSON file. For that, we should create a new file called cypress.env.json at the root of the project. We will not require the env keyword; instead, we can directly access them by passing the key-value pair.
{
"key1": "value1",
"key2": "value2"
}
Let us look into how to assign them in our cypress.env.json file.
As you see above, we have created a new file, cypress.env.json, and added our URL properties. The way of accessing the environment variables would be the same as mentioned above in the previous section.
Cypress JSON Reporter
As we know, Cypress is built on top of Mocha; any reporters that are built for Mocha can be used. We can configure reporter in our JSON file globally in our cypress.json file.
reporter
spec
Here, you can specify the reporter that should generate during the cypress run. It is set to spec as the default reporter.
reporterOptions
null
This is to specify the supported options for the reporter.
The options mentioned above are the configurations set in reporter by default. In addition, the spec reporter is set by default. Thus, in the reporter, we can set any reporter that is compatible with Mocha. reporterOptions is to specify the supported options depending on the reporter we are configuring.
Let's see how to configure the reporter in our cypress.json file.
Let us consider the multi reporter mochawesome as our reporter. We will first install the reporter and add them to our cypress.json file.
We will understand each of the properties in detail.
reporter: The name of the reporter which we are configuring in our project
reportDir: The directory where we are going to output our results.
overwrite: This flag asks for overwriting the previous reports.
html: Generates the report on the completion of the test.
json: Whether to generate a JSON file on test completion.
Cypress reporter in the cypress JSON file
Cypress package-lock.json
The package-lock.json file is created automatically for any operations when npm modifies the node modules or the package.json file. When we add any options or install any new dependencies to our Cypress package JSON file, then Cypress package-lock.json gets updated automatically.
Cypess package.lock JSON file traces every package and its version so that the installs are maintained and updated on every npm install globally. So in our Cypress package JSON file, when we update the version or add any dependency, package-lock.json also gets updated, and we don't want to make any alterations to it.
In the last article, we saw what Cypress is, its architecture, the installation process, and the pre-requisites required for the installation. However, before we begin writing our tests, we need to install Cypress. Click here to get the detailed steps to install Cypress.
Cypress Example
Cypress Example
This article will discuss the Cypress example, JSON examples, variables and aliases, and how to write test cases. So let’s get started.
Initially, when we open our Cypress Test Runner, a cypress.json configuration file is created. We tend to store the snippet that provides a way to implement and keep some properties that help in the program or automation code to execute. Similarly, Cypress also has a JSON file to store any values that we supply as configuration properties.
Cypress already has some default configuration values assigned. There are several options that we can customize based on our requirements. For instance, we can provide the baseURL property in our cypress.json file. So, every time we run the script, the baseURL is set and triggers.
Option
Default values
Description
baseUrl
null
This option can be used as a URL prefix for the cy.request()orcy.visit() commands.
watchForFileChanges
true
This option is set as true by default. It watches the files for changes and restarts them when any modifications are made.
Below is the snapshot that we have modified baseURL and watchForFileChanges properties in our Cypress.json file.
Note: We will be discussing all the options in Cypress Configuration as a separate topic later.
cypress.json file
Open Cypress
Earlier, we saw about how to create a Cypress project. Here, we will see how to open and execute the Cypress Test runner. So let’s dive in!
If you had installed Cypress via npm, then it has been installed in the ./node_modules directory. Hence, we can open our Cypress test runner by passing an npm command from the root of our project directory.
We can open Cypress in one of the following ways
1. by giving the full path command
./node_modules/.bin/cypress open
2. by using the shortcut for npm bin
$(npm bin)/cypress open
3. by using npx
Here npx is supported only with npm > v5.2, or we can install it separately.
npx cypress open
4. by using yarn
yarn run cypress open
Now we will see how to open Cypress by passing the full path command in our terminal.
1. You have to pass the command that is mentioned above in point 1, and you can see the following in the terminal
Opening Cypress in Terminal
2. After a moment, we can see the Cypress Test runner launching and will be able to view the Test runner as shown below. After the test runner has launched, you could see some sample test cases. Cypress has created a test folder in our project root that helps us with the basic setup and writing of test cases.
Example files in Test Runner
Now let us go back to VS Code. First, you can view some folder structures that got populated. Now let us break down each of the folder structures and look at them in detail.
Folder Structure in Cypress
As we see, Cypress has created a folder structure in our code editor. We will discuss them in detail.
Folders in Cypress Example
Fixtures – The fixture folder contains the data that is static and is reusable throughout the project. One best practice is not hardcore data(like credentials, test messages) in our tests. Instead, we access them in via a JSON, CSV, or HTML file. We should create our data files under the fixture folder. We access this file in our test using cy.fixture command.
Integration – Under this folder, we write the actual test cases that we usually call a spec file. Inside the integration folder, we can create multiple folders and many test files under each folder based on our project requirements. You can also see some default spec files produced with some examples.
Plugins – Plugins folder contains files that enable you to tap, access, and modify Cypress’s internal working behavior. With plugins, you can write the custom code that can help you execute the test commands that have direct access to every part(before or after execution) of your code structure. By default, Cypress creates the plugins in this path cypress/plugin/index.js
Support -Under the support folder, we have the files that help us provide standard or reusable methods. Before every spec run, Cypress executes the Support folder. So it is not necessary to import the support file in every other spec file. This folder is the right place to add reusable methods or global functions essential to be used over all the other spec files.
Node modules – This folder contains all the npm packages that we installed. Node modules are significant to run any node project. All the functions that are in our Cypress project are written inside our node modules folder. We will not be modifying any files inside node modules.
Cypress.json – We can add multiple configurations in our Cypress.json file. For instance, we can add environment variables, baseURL, timeouts, or any other options to override the default options in the Cypress configuration file.
Variables and Aliases
We shall discuss the variables and aliases in Cypress in detail.
As we understand and learn Cypress, it might be difficult for us to understand the Asynchronous API nature in Cypress. But as we see many examples in the future, it would become a piece of cake. Many modern browsers use asynchronous APIs, and even core Node modules are written with asynchronous code. Moreover, Asynchronous code is present everywhere in Javascript code. Therefore, we will be looking into the return values in Cypress.
Return Values In Cypress
All the Cypress commands are enqueued and runasynchronously. Therefore, we cannot assign or interact with any return values of any Cypress commands. We will see a little example of the same.
const button = cy.get("login-btn"); //this command is to get the element with the button attribute
button.click()
Closures
We cannot access the attributes of the button via the command mentioned above. Instead, we can yield Cypress commands by using .then(). We call these closures.
.then() helps you to access the thread that is yielded from the previous command. If you have understood native promises, it is the same way .then() works with Cypress. We can also nest different commands inside .then(). Each nested command can access the previous command in the loop. We will see that with an example.
cy.get('login').then(($btn) => {
// store the button's text
const buttonText = $btn.text()
// we are comparing the two buttons' text
// and ensuring they are different
cy.get('login').should(($btn2) => {
expect($btn2.text()).not.to.eq(buttonText)
})
})
We have used Closures in the above example, enabling us to keep the reference of the previous command in the loop.
Variables
Usually, we tend to assign a value to a variable. But in Cypress, we barely use const, var, and let. When we are using closures, we can access the yielded objects without assigning them to a variable.
But there are some cases where we can declare a variable is when the object’s state changes (mutable objects). For example, if we require comparing an object to its previous value, we will be declaring them to a variable to compare it with the next value. Let us look into an example for this.
<button>increment</button>
You clicked button <span id="num">0</span> times
Here, the span with the count 0 keeps increasing each time we click the button. So the button object tends to change its state every time.
Now let’s look into how we can assign this to a variable in our Cypress code.
cy.get('#num').then(($span) => {
// we are capturing the number by assigning it to a variable
const num1 = parseFloat($span.text())
cy.get('button')
.click() //we have clicked the button once
.then(() => {
// we are capturing the number again by assigning it to another variable
const num2 = parseFloat($span.text())
// we are ensuring that num1+1 is equal to num2
expect(num2).to.eq(num1 + 1)
})
})
Since span is changing its state every time we click the button, we can assign it to a variable to compare its present and previous state. Only in the case of mutable objects will we require variables, and using const is a good approach.
Aliases
Previously, we saw what variables and its limitation in Cypress is. To overcome this limitation, Aliases comes into the picture. Alias is one of the powerful constructs in Cypress. We will look into this in detail with examples.
Generally, Aliases can help you to work as a variable. However, there are some use cases where an alias can help us instead of a variable.
1. Reuse DOM Elements
We can alias DOM elements and later access them for reuse. Aliases also overcome the limitations of .then() command.
2. Sharing context
In simple meaning, sharing context is sharing the object between the hooks and the tests. The primary use case for sharing context is dealing with cy.fixture – which is to load a fixed set of data in a file.
How to access aliases?
Here, we will see how to access aliases. We will be using the .as() command to assign the element for later use. The required parameter is the alias name. The alias’ name is used as a reference within a cy.get() or cy.wait() using the @ prefix.
We will look into an example of how to access aliases.
In the first line, we are getting the id of the user_login from DOM. We are then using .as() and declaring with a name username. In the second line, we are accessing our alias with @ symbol and performing the type action.
Cypress Test Example
We will begin writing our first test case with Cypress. It is very simple and easy. But before that, we will look into the constructs of the Cypress test.
Basic Cypress Constructs
Cypress has adopted the Mocha syntax for its test cases and uses all the options Mocha provides. Below are the basic Cypress constructs that are used in common for our test cases.
Describe() – combines all the test cases into a single larger test and groups them together. It takes two parameters – the description of the test and a callback function.
it() – we write individual test cases in our it() block. This block also takes two parameters- what a test does, and the second parameter is the callback function.
after() – this executes after all the tests in the spec file.
afterEach() – this runs after every individual test cases.
before() – runs before all the tests in the spec file.
beforeEach() – executes before each individual test cases.
How to write a test case?
Before we begin, we should know what a test case is, write it, and what the steps are for a basic test case.
1. Pre-requisite – The state of the application that we are going to test.
2. Action – We perform some action on the application.
3. Assertion – We assert or validate the changed behavior concerning our actions.
We will consider LamdaGeeks application for our test examples. With the same procedure, we will consider automating the following scenario
2. Validate whether the title is Home – Lambda Geeks Cypress uses cy as its type definition. We will be appending the cy command to invoke any methods. Firstly, let’s create a new file in our code editor.
1. Create a new folder named Demo under the integration folder.
Creation of new folder
2. Under the Demo folder, create a new file sample.js. We will write our first test case in this file.
Creation of new file
Now let us commence writing our test cases!
1. 1. Firstly, we will visit the page using the visit() method in Cypress. This command will navigate to the URL that we provide. We will encompass them inside a describe() and it() block.
//type definition for Cypress object 'cy'
/// <reference types="cypress" />
describe("Testing the application", function() {
it("launch the application", function() {
// visit the lambdageeks page
cy.visit('https://lambdageeks.com/')
2. Once the application is open, we will validate the title using the get() method in Cypress .get() fetches all the css selectors from the DOM.
We are accessing the title using the title() method, and we are asserting using the Chai library with the command should by passing the first parameter as equal, in short eq. The second parameter is the string which we are expecting.
cy.title().should('eq','Home - Lambda Geeks')
Hurray! With two simple steps, we have written our Cypress Test case.
Here is the complete code of our test case
//type definition for Cypress object 'cy'
/// <reference types="cypress" />
describe("Testing the application", function() {
it("launch the application", function() {
// visit the lambdageeks page
cy.visit('https://lambdageeks.com/')
// validate the title
cy.title().should('eq','Home - Lambda Geeks')
});
});
Cypress Example: First test case
Cypress Login Example
We will see examples of how to automate a login page using Cypress. As we saw before, writing a test case in Cypress is simple. Now let us jump into setting the values in the text field and assertions.
2. Enter username in the username field using type command. We will be passing the username as a string in the type as a parameter.
cy.get('#username').type('test123')
3. Similarly, we write the same command to enter the password
cy.get('#password').type('123')
4. Now, we click the login button using the click() method in Cypress.
cy.get('#log-in').click();
5. After logging in, we land on the app page. So we assert that the URL has the /app extension using the .include() keyword in chai. The first parameter of should is the keyword we are asserting, and the second parameter is the expected result.
cy.url().should('include', '/app')
We have written a complete code for a login functionality in five simple steps. Below is the complete code for your reference.
//type definition for Cypress object 'cy'
/// <reference types="cypress" />
describe("Testing the application", function() {
it("should login with username and password", function() {
// visit the lambdageeks page
cy.visit('https://demo.applitools.com/')
cy.get('#username').type('test123')
cy.get('#password').type('123')
cy.get('#log-in').click();
cy.url().should('include', '/app')
});
});
Login Test Case
Solutions to Common Problems while Launching Cypress & FAQ
There might be some common problems we will face when we are trying to launch Cypress. We will discuss some of the common issues.
1. Cannot find ‘Cypress’ command during Open Cypress command
Once after installation, we should pass the open cypress command from the root of the project directory. For instance, you have created a project named CypressProject; you should pass the npm init command by navigating to the CypressProject folder. You can navigate by giving the below command in the terminal
cd CypressProject
Once you are at the root of the project, then pass the npm init command to download the dependencies.
Now to open Cypress, some people try to navigate to the /node_modules folder and ./bin and then pass the cypress open command. However, it does not work this way. So instead, give the whole open command from the root of the project directory to open Cypress.
./node_modules/.bin/cypress open
Also, be cautious about the slash ‘/’. Always provide this ‘/’ to open Cypress.
2. Cypresscannot be loaded because running scripts is disabled on thissystem
When you are installing Cypress in windows, you might sometimes face the above error. It is because due to a security exception. We can solve this error by passing the below command in PowerShell.
Set-ExecutionPolicy RemoteSigned
Steps to reproduce:
Open PowerShell
Run this command Set-ExecutionPolicy RemoteSigned
Type Yes
Now open the Cypress by passing ./node_modules/.bin/cypress open. Test Runner will open now.
FAQ
1. Which are the operating systems supported by Cypress?
Cypress supports Mac, Windows, and Linux operating systems.
2. Does Cypress support automation in native mobile apps?
Cypress will not be able to run on a native mobile app ever. But we can test mobile applications that are written in a browser like Iconic Frameworks.
3. Whether Cypress supports only Javascript-based applications?
No! Cypress can interact with any web browser written in languages like Ruby on Rails, Node, PHP, C#. But we will be writing our tests in Javascript. Otherwise, Cypress can interact with any front end, back end, language, and framework.
In this tutorial, we will discuss the Cypress Automation Framework in detail. We will be covering what Cypress is, how it is different from other testing frameworks, the architecture of Cypress, and the installation procedure in this article. Cypress is an exciting topic and is fun to learn too. Let’s begin!
Cypress Automation Framework
Cypress Automation Framework is a pure Javascript-based testing tool that mainly focuses on front-end testing in modern web applications. With Cypress, applications are easy to test with the visual interface to witness the test execution. Thus, Cypress comes as a boon for both developers and QA engineers by making script writing and test execution easy. In addition, it comes with a distinctive test runner, which makes DOM manipulation easy and runs directly on the Browser.
Table of Content
What is Cypress?
Cypress is faster, better and provides definitive testing that runs on a browser.Cypress is mainly compared with Selenium, but it is completely different. Cypress does not run on top of Selenium, which means it is completely independent. Instead, Cypress runs on top of Mocha, which is again a Javascript-rich test framework. It is compatible with only the Chai Assertion Library, which can access a wide range of BDD and TDD assertions.
Cypress mainly focuses on three different types of testing. They are End-to-End tests, Unit tests, and Integration tests. Cypress can execute any tests that can run in a browser. In addition, it comes along with different mocking capabilities and validations that are enthralled towards front-end testing.
The browsers that Cypress supports are Chrome, Firefox, Edge, Electron, and Brave. Moreover, cross-browser testing is easily achievable with Cypress. Finally, though Cypress supports only Javascript, it can also be written with Typescript, primarily written with Javascript.
Cypress Automation
Cypress is an open-source tool with a free Test runner but has pricing ranging for teams and businesses where they charge you for the Dashboard. However, Dashboard is free up to some extent, unless you additional features like Flake detection, Email support, Jira integration, and many more.
Cypress is mainly used to automate scripts on the web(can automate anything that runs on a browser). It can never run on native mobile apps but can automate some of the functionalities of the mobile applications if those are developed in a browser.
Features
There are many awesome features available in Cypress that stand out from any other automation tool. Here, let’s discuss some of the main features, and we’ll get introduced to other parts later once we begin writing our test cases!
Automatic waiting – Cypress has the advantage of automatic waiting. We will never need to add force waits and sleeps for waiting for the DOM to fetch the element. Cypress automatically waits for any interaction with elements and execution of assertions. Thus, tests are fast!
Time travel – Cypress captures screenshots during test execution. We can view the results visually in real-time by just hovering on the executed commands in the Dashboard. This way, the tests are easier to debug
Debugging tests – Cypress can debug tests from popular tools like Developer tools. The errors are readable, and stacks are easily traceable.
Stub requests – Cypress has options to confirm and control function behaviors, network responses, or timers used by stubs and spies.
Continuous Integration – Cypress does not depend on any other additional CI services. However, on running the command for the test, integration is easily accessible.
Myth about Cypress
There is a myth that Cypress can run only on Javascript-friendly web applications. However, Cypress can test any web applications built with Django, Ruby on Rails, Laravel, etc. In addition, Cypress supports any of the programming languages such as PHP, Python, Ruby, C#, etc. However, we write our tests in Javascript; beyond that, Cypress works on any application.
Components of Cypress
There are two main components in Cypress. They are Test Runner and Dashboard.
Cypress Test Runner
Cypress Test Feature
Test Runner – Cypress provides this unique test runner, where the user can view the commands during execution and application under test.
There are few subcomponents under Test runner. They are
Command Log – This is a visual representation of the test suite. You can see the commands executed in the test, the assertion details, and the test blocks.
Test Status menu – This menu shows the number of test cases that passed or failed and the time taken for execution.
URL Preview – This gives you information about the URL you are testing to keep track of all the URL paths.
Viewport sizing – You can set the app’s viewport size for testing different responsive layouts
App Preview – This section displays the commands that run in real-time. Here you can use Devtools to debug or inspect each base.
Dashboard: Cypress Dashboard gives the ability to access the tests that are being recorded. With Dashboard service, we can witness the number of passed, failed, or skipped tests. Also, we can view snapshots of the failed tests by using cy. screenshot() command. You can also witness the video of the entire test or the clip of the failed tests.
Cypress Architecture
Most of the testing tools run on the server outside the Browser and execute commands over the network. But, Cypress runs on the Browser where the application is also running. This way, it can access all the DOM elements and everything inside the Browser.
Node server runs behind the Cypress on the client-side. Thus, the node server and Cypress interact with each other, accompany and carry out tasks to support the execution. Since it has access to both the front and back end, the responsiveness to the application in real-time during execution is well accomplished and can also perform tasks that even run outside the Browser.
Cypress Architecture
Cypress also interacts with the network layer and captures commands by reading and changing the web traffic. Finally, Cypress sends HTTP requests and responses from the node server to the Browser. Since Cypress operates in the network layer, it helps to modify the code that might interfere with the automation of the web browser. The communication between the Node server and Browser is via the WebSocket, which begins execution after the proxy is started.
Cypress controls all the commands that run in and out of the browsers. Since it is installed in a local machine, it directly interacts with the operating system to record videos, capture snapshots, accesses the network layer, and performs file system operations at ease. Cypress can access everything from DOM, window objects, local storage, network layer, and DevTools.
Install Cypress
This section will discuss the installation process that needs to be followed before writing our test cases. There are two different ways to download Cypress. They are
Install via npm
Direct Download
Before we install Cypress, we might need a few pre-requisites to kick start to install via npm. Let’s see them in detail.
Pre-requisites
We will require certain pre-requisites before writing our test cases.
As discussed above, Cypress runs on a node server; hence we will have to install Node.js.
Also, to write our test cases, we need a code editor or IDE.
In this example, we will be using Visual Studio Code. So let’s dive into the details.
Node.js Installation in Mac
Here, we shall discuss the steps to download Node.js in Mac. Navigate to https://nodejs.org/en/download/. You will now land on the download page.
Node package in macOs
1.Click on the macOS Installer. On clicking, You can find a package file downloaded below. Click on the pkg file to install Node.js
Installer introduction
2. Once you click the .pkg file, the Node installer will open. The introduction section gives you the Node.js and npm versions. Click on Continue
Agree License
Allow Access in Installer
3. Click on Agree Button and then Continue. A pop-up will appear to allow access to your files in the Download folder. Click on Ok.
Choose Destination
4. In this section, you can select the destination to which Node.js has to be downloaded. Again, you can choose according to your system space. Here I am choosing the default location.
Installation Type
Enter Username and Password to Install
5. Click on the Install button. Once you click, a pop-up asking your system password would arise. Enter your password and click on Install Software.
Installation Summary
6. Hurray! We have installed Node.js and npm package. Click on Close to finish installing.
Visual Studio Code Installation in Mac
We have successfully installed Node.js. Now let us install our code editor Visual Studio Code. VS code is a powerful tool that has all the in-built functionalities of Javascript. So let’s dive into the installation steps of Visual Studio Code.
Here we will discuss the steps to download VS code in Mac. First, navigate to https://code.visualstudio.com/download to land on the download page of VS code.
VS Code Install in Mac
1. Click on the Mac icon. You can see a package getting downloaded below.
Installed Package in zip
2. Click on the downloaded file to unzip the package. Once unzipped, you can find the Visual Studio Code in your Downloads in Finder.
VS Code in Downloads
3. Hurray! We have downloaded our Code editor. Click on the icon to open Visual Studio Code.
Creation of a new Cypress project
We will now see how to create a new node project in our Visual Studio Code. Once you click on the VS code icon, you will land on the Welcome page. Next, click on the Add Workspace folder to create a new folder.
Creation of new project
Once you click on the folder, you will get a pop-up asking to add a new folder. Now click on the location you want to add the workspace. Next, click on New Folder and Add the Folder name as CypressProject and click Open.
New folder Creation
Now we have created a folder for our Cypress test. Before we begin writing our tests, we should install the package.json file. Before installing, let us understand what is package.json file.
What is Package.json file?
Package.json comprises all the npm packages in a file, usually located in the project root. It is commonly located in the root directory of the Node.js project. This file holds all the applicable metadata necessary for the project. It gives all the information to npm and helps in identifying the project and handle dependencies. Package.json file contains information such as project name, versions, license, dependencies, and many more. Now we have understood what is package.json file. So, let’s begin the steps to download the file in our Visual Studio code.
Open Terminal
1. To execute our commands, we need to open the Terminal. On the top of the VS code, click on the Terminal. Once the dropdown opens, click on New Terminal.
Install package.json file
2. Once the terminal opens, type the below command in the project directory and press Enter.
npm init
3. Once you press Enter, you can see the certain information displayed. You can type the required details in the Terminal and press Enter to obtain all the fields.
Project details
Package name: You can provide any name to your package. I have left it blank as it is pre-populated with the folder name we created.
Version: This gives the information of the version of npm. You can skip this and press Enter.
Description: Here, you can give a piece of additional information to the package. If required, you can type the description and press Enter again.
Entry point: This represents the entry point of the application. Since it is pre-populated with index.js, we can skip this field and press Enter.
Test command: Command that is given to run the test. Here it is not necessary to give any commands, but if required, you can definitely provide any command.
Git repository: This field requires the path to the git repository. You can leave this field blank as well.
Keywords: Unique keywords to help identify the project. You can skip this field too.
Author: This is usually the username of the person. You can add your name and press Enter.
License: License is pre-populated with ISC. You can proceed by pressing Enter.
4. Once you press Enter, Terminal will ask for confirmation by listing all the details you provided. Type Yesand press Enter again.
Package.json file creation confirmation
We have now generated a package.json file. You can view the file in your code editor with the information we provided.
Created Package.json file
Installation steps of Cypress
We have installed all the pre-requisites for our Cypress download, node, and initialized npm. As mentioned above, there are two ways to download Cypress.
Download Cypress via npm
You will have to pass the below-mentioned command in the Terminal to install Cypress. In addition, you will have to give the command in the project directory to install the node and generated the package.json file.
npm install cypress --save-dev
Cypress Installation command
Once you pass the command, it will download all the relevant dependencies required for the project. At the writing of this article, the latest version of Cypress is 7.7.0. The version might differ at the time you are downloading.
Successful Cypress Installation
With reference to the above image, you can see that we have downloaded Cypress. You can verify by the downloaded representation in Terminal and the addition of devDependencies in the package.json file.
Direct Download
We can download Cypress directly from their CDN if you are not using the Node or npm package in the project. However, recording the tests in the Dashboard is not possible via direct download.
You can download by clicking on download Cypress directly from this link. This will now directly download the package. Once the package is downloaded, open the zip file and double click. Cypress will run without the need for any installation of dependencies. This download will always pick up the latest version based, and the platform will be detected automatically. However, downloading Cypress via npm is recommended rather than a direct download.
For more post on Technology, please visit our Technology page.
Puppeteer is an open-source node js library and is used a web automation as well as web scraping tool. You need the basic understanding of Javascript, and HTML DOM structure to start working with Puppeteer. This Puppeteer tutorial series is distributed in the below segments which will equip you with all the necessary experience to start working with Puppeteer.
In this “Puppeteer Browser Class” tutorial, we will have in depth understanding further about the below mentioned classes which consists of the important namespaces, events, and other exhaustive methods that are needed to work with Puppeteer web scraping techniques.
Puppeteer BrowserFetcher Class is used to download and manage the different browser versions. BrowserFetcher class operates on a revision string that specifies the version of the chrome browser. The revision number can be obtained from here. In the case of Firefox, it downloads the browser nightly based on the version number.
It is not possible to work simultaneously with another instance of BrowserFetcher class. The frequently used methods of BrowserFetcher class are explained in the next sections.
Puppeteer BrowserFetcher Class – Methods:
Below methods are available in puppeteer browserfetcher class,
browserFetcher.canDownload(revision) – With the help of the revision number of the browser, this method checks the availability of the specified browser as a part of the header request. The method returns the boolean value(true or false) based on availability.
browserFetcher.download(revision[, progressCallback]) – This method downloads the chrome browser using the revision number argument. Here progressCallback is an optional argument that calls the function with two arguments – downloaded bytes and total bytes. This method returns the revision information as a promise object.
browserFetcher.host() – It returns the hostname, which is used for downloading of browser.
const hostName = browserFetcher.host();
browserFetcher.localRevisions() – It returns the list of all revisions which are available in the local system.
const revList = browserFetcher.localRevisions();
browserFetcher.platform() – It returns the platform name of the host, which will be any of the mac, Linux, win32, or win64.
const platformName = browserFetcher.platform();
browserFetcher.product() – It returns the browser name which will be either chrome or firefox
const productName = browserFetcher.product();
browserFetcher.remove(revision) – This method is used to remove the specified revision for the current product/browser. It returns the promise object, which is resolved after completion of the process.
const revInfo = browserFetcher.remove(‘766890’);
browserFetcher.revisionInfo(revision) – It will return an object on revision information which includes revision, folderPath, executablePath, url, local, and product.
The frequently used events and methods of Browser class are explained in the next section.
Puppeteer Browser Class – Events:
Below events are available in browser class,
browser.on(‘disconnected’) – This event is triggered when the browser is closed/crashed or browser.disconnect method is called.
browser.on(‘targetchanged’) – This event is triggered when the url of the target has changed.
browser.on(‘targetcreated’) – This event is triggered when the new page opened in a new tab or window by the method browser.newPage or window.open.
browser.on(‘targetdestroyed’) – This event is triggered when the target is destroyed, i.e., the page is closed.
Puppeteer Browser Class – Methods:
Below methods are available in browser class,
browser.browserContexts() – It returns the list of all browser contexts. For a newly launched browser, this method will return the single BrowserContext instance.
browser.close() – This method is used to close all the open chromium-browser pages.
await browser.close();
browser.createIncognitoBrowserContext() – It creates/returns the incognito browser context, which will never share the cookies or cache with any other browser contexts. In the below example, the web page(google) will be opened in incognito mode.
browser.defaultBrowserContext() – It returns default browser context which can not be destroyed or closed.
browser.disconnect() – It will disconnect the browser from the puppeteer. But, the browser will remain running in this case.
browser.isConnected() – This method checks if the browser is connected or not. It will return boolean values based on the check.
const boolFlag = await browser.isConnected();
browser.newPage() – This method will create a new page and return the instance of the page.
const page = await browser.newPage();
browser.pages() – This method returns the list of all pages which are currently in the open state.
const pageList = await browser.pages();
browser.process() – This method returns the created browser process. If the browser is created using browser.connect method, and it will return a null value.
browser.target() – This method returns the target associated with the browser.
const target = await browser.target();
browser.targets() – It returns the list of all active targets within the browser.
const targetList = await browser.targets();
browser.userAgent() – It returns the promise object about the original agent of the browser.
browser.version() – It returns the version of the browser in the format of ‘HeadlessChrome/xx.x.xxxx.x’ for headless chrome and ‘Chrome/xx.x.xxxx.x’ for non headless chrome. The format can change in a future release.
browser.waitForTarget(predicate[, options]) – It will search in all the browser contexts and wait for the target.
browser.wsEndpoint() – It returns the web socket url of the browser.
const wsUrl = await browser.wsEndPoint();
Reference: Click here to learn more on Browser class events and methods.
Puppeteer BrowserContext Class
The BrowserContext class helps to operate multiple browser instances. After launching a browser instance, by default, a single BrowserContext is used. The browserChrome.newPage() method creates a page in the default BrowserContext class object. If a web page invokes another page, then the new page should belong to the browsercontext of the parent page. Here, the new page can be created using the window.open() method.
In the below example, Puppeteer has the ability to create a browser context in ‘incognito’ mode. The ‘incognito’ browser context does not write any data in the storage.
// Incognito browser context creation
const contextIncognito = await browserChrome.createIncognitoBrowserContext();
// New page creation through the browser context.
const pageChrome = await contextIncognito.newPage();
await pageChrome.goto('https://www.google.com');
//close context after use
await contextIncognito.close();
The frequently used events and methods of BrowserContext class are explained in the next section.
Puppeteer BrowserContext Class – Events:
Below events are available in browsercontext class,
browserContext.on(targetchanged) – This event is triggered when the url of the target within the browser context has changed.
browserContext.on(targetcreated) – This event is triggered after creation of inside the browser context. The methods window.open and browserContext.newPage are responsible for this event.
browserContext.on(‘targetdestroyed’) – This event is triggered when the target is destroyed within the browser context.
Puppeteer BrowserContext Class – Methods:
Below methods are available in browsercontext class,
browserContext.browser() – This method returns the browser object which is available within the browser context.
browserContext.clearPermissionOverrides() – This method removes all permission overrides from the browser context. The below example shows how to use this method –
browserContext.close() – This method is used to close or destroy the browser context. All the browsers available within the browser context will be closed.
browserContext.close();
browserContext.isIncognito() – This method is used to check if the browser has been created in ‘incognito’ mode or not. It returns a boolean value(true – incognito mode or false – non-incognito mode) based on the browser mode. By default, any browser is invoked in ‘non-incognito’ mode.
browserContext.newPage() – This method is used to create a new page in the same browsercontext.
browserContext.newPage();
browserContext.overridePermissions(origin, permission) – This method is used to grant the specified permission to the origin, i.e., the target url. The different permissions which are available to grant are –
browserContext.pages() – This method returns the list of all the open pages available in the browser context. Any non-visible page will not be listed here.
const openPageList = browserContext.pages();
browserContext.targets() – This method returns the list of all the active targets available in the browser context. Any non-visible page will not be listed here.
browserContext.waitForTarget(predicate[, options]) – This method is used to wait for a target to have appeared and returned the target object. The argument, ‘predicate’ is basically a function call for each of the targets. Also, optionally, we can pass some configuration values such as timeout as a second argument.
Reference: Click here to read more on BrowserContext class events and methods.
Conclusion:
In this “Puppeteer Browser Class” tutorial, we have explained the BrowserFetcher class, BrowserContext class, and Browser class which includes the important namespaces(if any), events(if any), and methods that are frequently used in Puppeteer web scraping techniques with examples. In the next article, we will explain Page, Frame, and Dialog class.
Puppeteer which is an open-source node js library, can be used as a web scraping tool. Understanding of command line, Javascript, and HTML DOM structure should be good to start with this puppeteer tutorial. The Series of Puppeteer tutorial is distributed among below Sub section to get a good hold on Puppeteer.
In this “Puppeteer Class” tutorial, we will explain the below classes which include the important namespaces(if any), events(if any), and methods which are frequently used in Puppeteer web scraping techniques.
We will explain important components with examples throughout this article.
Puppeteer Class
Conceptually, the class is a blueprint of an object which defines a set of instructions( variables and methods). Here, the Puppeteer class is defined using javascript to perform different actions to perform web scraping. Let’s check the below example, the Puppeteer class module has been used to launch a Chromium web instance.
const puppeteer = require('puppeteer');
(async () => {
const browserChrome = await puppeteer.launch();
const pageChrome = await browserChrome.newPage();
await pageChrome.goto('https://www.google.com');
// We can write steps here
await browserChrome.close();
})();
Puppeteer class also provides multiple Namespaces and Methods, which supports the web scraping process. The frequently used Namespaces and Methods are explained next sections.
Puppeteer Class – Namespaces:
It’s a container that defines multiple identifiers, methods, variables, etc., in javascript. It’s a way to group the code in a logical and organized way. Below namespaces are provided by the Puppeteer class.
puppeteer.devices: It returns a list of devices that can be used by the method page.emulate(options) to perform scraping in mobile devices.
Example – Open and close google web page on a mobile device –
puppeteer.errors: While working with different puppeteer methods, there is a chance of exceptions. Mostly, if the methods are unable to fulfill the requests, it throws errors. There are different classes defined to handle errors through the ‘puppeteer.errors’ namespace.
Example – for method page.waitForSelector, if the web element does not appear within the specified time, then the timeout error will appear. Please go through the below example, which shows an approach to handle timeout,
try {
await page.waitForSelector('<web-element>');
} catch (err) {
if (err instanceof puppeteer.errors.TimeoutError) {
// Write code to handle the timeout error.
}
}
puppeteer.networkConditions: It returns a list of network conditions that can be used on the method page.emulateNetworkConditions(networkConditions). The complete list of network conditions is defined here.
Example – Through this code sample, we will open the google web page using a pre-defined network condition.
puppeteer.product: It returns the name of the browser, which will be used for automation(Chrome or Firefox). The product for the browser is set by either environment variable PUPPETEER_PRODUCT or the product option available in puppeteer class method puppeteer.launch([options]). The default value is Chrome.
Reference: Click here to learn more on Puppeteer Class namespaces.
Puppeteer Class – Methods:
Methods contain statements to perform the specific action. The puppeteer class have below methods,
puppeteer.clearCustomQueryHandlers() – It clears all registered handlers.
puppeteer.connect(options) – This method is used to connect puppeteer with any existing browsers. It returns an object of type promise which indicates the status of this asynchronous process. Example – In the below example, puppeteer disconnect from the current browser and reconnect,
const puppeteer = require('puppeteer');
(async () => {
const browserChrome = await puppeteer.launch();
// Copy the endpoint reference which will be reconnected later
const endpoint = browserChrome.wsEndpoint();
// Disconnect puppeteer
browserChrome.disconnect();
// Use the endpoint to re-connect
const browserChrome2 = await puppeteer.connect({endpoint});
// Close second instance of Chromium
await browserChrome2.close();
})();
puppeteer.createBrowserFetcher([options]) – It creates a browser fetcher object to download and manage the different versions of browsers (Chrome and Firefox).
puppeteer.customQueryHandlerNames() – It returns an array of all the registered custom query handlers.
puppeteer.defaultArgs([options]) – It returns the default configuration options of chrome browser as an array while launching. Also, we can set the configurable options of a browser using the optional argument option.
const args = puppeteer.defaultArgs();
puppeteer.executablePath() – It returns the path is expected by the puppeteer for the bundled browser instance. The path that would not be available in the download was skipped by the environment variable PUPPETEER_SKIP_DOWNLOAD. Also, we can use the environment variables PUPPETEER_EXECUTABLE_PATH and PUPPETEER_CHROMIUM_REVISION to change the path.
const args = puppeteer.executablePath();
puppeteer.launch([options]) – This puppeteer class method is used to launch the web browser. Through the optional argument, we can pass the different configurations of the browser, such as product(browser name), headless, devtools, etc. This method returns the promise object, which holds the reference of the launched browser.
const browser = await puppeteer.launch();
puppeteer.registerCustomQueryHandler(name, queryHandler) – It’s used to register a custom query handler. Here “name” provides the name of the query handler, and “queryHandler” defines the actual custom query handler.
puppeteer.unregisterCustomQueryHandler(name) – It’s used to unregister any custom query handler.
Reference: Click here to read more on Puppeteer Class methods.
Target Class
The target class provides methods to work with targets. The most frequently used methods which are available with target class are explained in the next section.
Target Class – Methods:
Below methods are available in targets class –
Target.browser() – It returns the browser object which is linked to the target.
Target.browserContext() – It returns an object of type browserContext which is linked to the target.
Target.createCDPSession() – It creates and returns the devtool protocol session of the chrome, which is attached to the target.
Target.opener() – It returns the target which opens this target. Basically, this method is used to get the parent target. It returns null for the top-level target.
Target.page() – It returns the page object of the target. If the type of the target is not a page, it returns a null value.
Target.type() – It’s used to get the type of the target. The return value can be either of the options – ’background_page’ , ‘page’ ,’shared_worker’,’service_worker’,’browser’ or ‘other’.
Target.url() – It returns the url of the target.
Target.worker() – It returns the webworker object. The return value is null if the target is neither ‘service_worker’ nor ‘shared_worker’.
Reference: Click here to read more on Target class methods.
ConsoleMessage Class
The ConsoleMessage class objects are dispatched by page through the console event. The frequently used methods of the consoleMessage class are explained in the next section.
ConsoleMessage Class – Methods:
Below methods are available in ConsoleMessage class –
consoleMessage.args() – It returns a array of JSHandler object. The JSHandler prevents the linked JS object from being garbage collected until the handle is disposed of. It’s automatically destroyed when the parent browser context is destroyed.
consoleMessage.location() – It returns an object of the resource, which includes the below parameters.
url – It denotes the URL of the known resource. If not known, it will keep an undefined value.
LineNumber – It’s the 0-based line number that is available in the resource. If not available, it will keep an undefined value.
columNumber – It’s the 0-based column number that is available in the resource. If not available, it will keep an undefined value.
consoleMessage.stackTrace() – It returns a list of objects(each object refers a resource) which includes below parameters.
url – It denotes the URL of the known resource. If not known, it will keep an undefined value.
LineNumber – It’s the 0-based line number that is available in the resource. If not available, it will keep an undefined value.
columNumber – It’s the 0-based column number that is available in the resource. If not available, it will keep an undefined value.
consoleMessage.text() – It returns the text of the console.
consoleMessage.type() – It returns the string as the type of console message. The type can be either of the values – log, debug, info, error, warning, dir, dirxml, table, trace, clear, startGroup, startGroupCollapsed, endGroup, assert, profile, profileEnd, count, timeEnd.
Reference: Click here to learn more on consoleMessage class methods.
TimeoutError Class
While working with different puppeteer, there is a chance of exceptions. Mostly, if the methods are unable to fulfill the requests, it throws errors. The TimeoutError class is used to handle this kind of exception.
Example of TimeoutError Class – for method page.waitForSelector, if the web element does not appear within the specified time, then the timeout error will appear. Please go through the below example, which shows an approach to handle timeout,
try {
await page.waitForSelector('<element>');
} catch (e) {
if (e instanceof puppeteer.errors.TimeoutError) {
// Write code to handle the error.
}
}
FileChooser Class
The file chooser class object is created using the method page.waitForFileChooser. The FileChooser class is used to interact with files. The frequently used methods of the FileChooser class are explained in the next section.
FileChooser Class – Methods:
Below methods are available for FileChooser class –
fileChooser.accept(file_with_path) – This method is used to upload any file (for which path is provided as an argument).
fileChooser.cancel() – This method is used to cancel the file upload process.
fileChooser.isMultiple() – This method checks if the fileChooser allows to select multiple values. It returns a boolean expression(true or false).
In this “Puppeteer Class” tutorial, we have explained the Puppeteer class, Target class, MessageConsole class and TimeoutError class which includes the important namespaces(if any), events(if any), and methods that are frequently used in Puppeteer web scraping techniques with examples. In the next article, we will explain BrowserContext, Browser, and BrowserContext Class.