The App
Let's take an application that has an <input type="color">
element. When the user picks a new color, the application changes a CSS variable which controls the background color. In action, it looks like this:
The HTML markup below has only the input color element.
<head>
<link rel="stylesheet" type="text/css" href="app.css" />
</head>
<body>
This is the body of the document
<div>Change color using this color input
<input type="color">
</div>
<script src="app.js"></script>
</body>
The app.css
file uses CSS variables to control the background color
:root {
--main-color: #fff;
--background-color: #000;
}
body {
color: var(--main-color);
background-color: var(--background-color);
font-size: xx-large;
}
Finally, the application code app.js
reacts to the change
event and sets the new CSS variable value
document.querySelector('input[type=color]')
.addEventListener('change', (e) => {
console.log('setting new background color to: %s', e.target.value)
document.documentElement.style.setProperty(
'--background-color', e.target.value)
})
Tip: find the application and test code in repository cypress-example-recipes under "Testing the DOM" recipes.
Tests
In our first test, we will ignore how the application implements the background color. Instead we will confirm that the change works.
Tip: Â instead of using RGB values to make the assertion, you can specify the expected value in different formats using chai-colors NPM module, see our recipe Adding Chai Assertions.
The above test is too short in my opinion. We want to go through the entire user action: visit the page, change the color using the input element, and observe the result. A more realistic test would be:
it('changes background color', () => {
cy.visit('index.html')
cy.get('body').should('have.css', 'background-color', 'rgb(0, 0, 0)')
// select the new color value in the <input type="color">
// element and trigger "change" event
cy.get('input[type=color]')
.invoke('val', '#ff0000')
.trigger('change')
// check the background color has been changed
cy.get('body')
.should('have.css', 'background-color', 'rgb(255, 0, 0)')
})
The test uses the .invoke command to set the value of the color input element, then calls the .trigger command to generate the "change" to the event. The application does the rest.
To better see how the .trigger
command caused the application to change the color, hover over the command - Cypress shows before and after snapshots that include all styles.
Spying on browser API
In the next test, let's confirm the implementation details. Our application calls the browser API method document.documentElement.style.setProperty
to change the CSS variable. Because Cypress tests run directly in the same browser's window as the application, we can spy and stub DOM method calls directly.
it('can spy on native methods', () => {
cy.visit('index.html')
cy.get('body').should('have.css', 'background-color', 'rgb(0, 0, 0)')
// create a spy and save it under an alias
cy.document().its('documentElement.style')
.then((style) => cy.spy(style, 'setProperty').as('setColor'))
cy.get('input[type=color]')
.invoke('val', '#ff0000')
.trigger('change')
cy.get('body').should('have.css', 'background-color', 'rgb(255, 0, 0)')
// find spy by its alias and confirm it was called as expected
cy.get('@setColor')
.should('have.been.calledWith', '--background-color', '#ff0000')
})
The test passes, and Cypress Command Log shows the table with the spy, its alias, and how many times it was called. It also shows when it was called among the other Cypress commands.
Tip: if you do not care about values of some arguments in an assertion like
cy.get('@setColor')
.should('have.been.calledWith', '--background-color', '#ff0000')
you can use Sinon.js placeholders. For example, skip the --background-color
value and match any string using:
cy.get('@setColor')
.should('have.been.calledWith', Cypress.sinon.match.string, '#ff0000')
See also
Read these blog posts that show more examples accessing the browser and DOM APIs directly from Cypress tests