Solving Test.Bash 2022 UI Challenge With Cypress

Step-by-step tutorial for testing a simple web application.

Recently I have decided to solve the Test.Bash 2022 UI Automation challenge. I need to test a small web application running at https://automationintesting.online/; it is a message form. A user can submit the form, and then the admin can log in and read the message. The total time to solve it is twenty minutes, so let's go.

Video

You can watch the video I submitted to the challenge below

Create a repo and a local project

I have created a repository for my tests bahmutov/test-bash-2022-ui. Locally I have created a new folder with Git and NPM package

1
2
3
4
5
6
# "md" is my alias for creating folders
# a combination of "mkdir" and "cd"
~/git $ md test-bash-2022-ui
~/git/test-bash-2022-ui $ git init
~/git/test-bash-2022-ui $ git remote add origin [email protected]:bahmutov/test-bash-2022-ui.git
~/git/test-bash-2022-ui $ npm init -y

Let's install Cypress and Prettier, cause I love automatic code formatting.

1
2
3
4
5
# all commands are in the "test-bash-2022-ui" folder
$ node -v
v16.14.0
$ npm i -D cypress prettier
# I copied .prettierrc.json from another project

Open Cypress and scaffold E2E tests

1
$ npx cypress open

Scaffold E2E tests

We are going to be testing the application running at "https://automationintesting.online/". Let's put this as baseUrl in our cypress.config.js file.

cypress.config.js
1
2
3
4
5
6
7
const { defineConfig } = require('cypress')

module.exports = defineConfig({
e2e: {
baseUrl: 'https://automationintesting.online/',
},
})

Time to write a test

Sending a message

If you have never seen a Cypress test, take a look at the Cypress introduction. We visit the base URL, find the message form, and then enter some text into the fields. To make sure we can find our message, I use the Lodash library (bundled with Cypress) to generate a random id to include in the message subject.

cypress/e2e/send.cy.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
it('sends a message successfully', () => {
const name = 'Test Cy User'
const email = '[email protected]'
const phone = '123-456-7890'
const subject = `Test Cy message ${Cypress._.random(1e5)}`
const body = Cypress._.repeat('message body ', 5)

cy.visit('/')
cy.get('.row.contact form').within(() => {
cy.get('[data-testid="ContactName"]').type(name)
cy.get('[data-testid="ContactEmail"]').type(email)
cy.get('[data-testid="ContactPhone"]').type(phone)
cy.get('[data-testid="ContactSubject"]').type(subject)
cy.get('[data-testid="ContactDescription"]').type(body)
cy.get('#submitContact').click()
})
// the form was submitted
cy.log('**checking messages**')

// Axios rejects a promise after checking auth route right away
cy.on('uncaught:exception', () => false)
cy.visit('/#/admin')
cy.get('[data-testid="username"]').type(Cypress.env('username'))
cy.get('[data-testid="password"]').type(Cypress.env('password'), {
log: false,
})
cy.get('[data-testid="submit"]').click()
cy.contains('.navbar', 'B&B Booking Management')
.should('be.visible')
.find('a[href="#/admin/messages"]')
.click()
cy.location('hash').should('equal', '#/admin/messages')
cy.get('.row.detail')
.should('have.length.greaterThan', 0)
.contains('.row', subject)
.should('have.class', 'read-false')
.click()
cy.contains('.message-modal', subject)
.should('be.visible')
.and('include.text', name)
.and('include.text', phone)
.and('include.text', email)
.and('include.text', subject)
.and('include.text', body)
.contains('button', 'Close')
.click()
cy.contains('.message-modal', subject).should('not.exist')
cy.contains('.row.detail', subject)
.should('have.class', 'read-true')
.and('not.have.class', 'read-false')
})

The test runs pretty quickly, as you can see in this video

Sending and checking the message test run

Of course, it is not a problem - we can always hover over the test steps to see how the application's DOM looked at that particular moment.

Time-traveling debugger shows the app at that test step

Tip: the demo application resets itself every 10 minutes. Thus it could potentially reset during the test run, causing the test to fail. To mitigate this, we can turn on Cypress test retries.

1
2
3
it('sends a message successfully', { retries: 2 }, () => {
...
})

If the test fails, Cypress will mark it "flaky" and retry it up to 2 more times.

In the next blog posts, I will explain how to improve this first test to understand any potential test failures better.

See also