- CSS pseudo-classes
- checkValidity
- validationMessage
- validity
- Form is not submitted
- Bonus 1: Match CSS class ":valid" and ":invalid"
HTML standard has nice built-in form element validation rules, widely supported by the modern browsers. No need to bring a 3rd party library just to require a form input to have a value, or for doing simple numerical checks. For example, if we want to ask the user for the item's name and its quantity, we can write:
1 | <form id="form-validation" action="/action_page.php"> |
Any user trying to submit this form while breaking the rules is going to see the error messages shown by the browser, and the form is not going to be submitted.
Notice how the browser stops the form submission on the first broken rule.
The error popups are shown by the browser - they are NOT part of the page's DOM. Thus we cannot query them from the Cypress test. How do we check our form elements from the end-to-end tests to ensure the rules are set up correctly?
CSS pseudo-classes
The HTML standard defines several CSS pseudo-classes for finding the invalid and valid input elements. For example the :invalid
pseudo-class is present on every input element currently breaking its validation rules.
We can write a Cypress test to check the presence of form elements using this class.
🧭 You can find these form validation examples at https://glebbahmutov.com/cypress-examples under "Recipes" section. You can also find more information about form validation by searching the Cypress docs
1 | /// <reference types="cypress" /> |
Notice that the input elements satisfying its rules get pseudo-class :valid
. At the end of our test, all input elements have the pseudo-class :valid
, including the submit button.
checkValidity
Unfortunately, we cannot check :invalid
or :valid
pseudo-class by using an assertion, since it is not a declared class.
1 | /// <reference types="cypress" /> |
Instead we can call a method checkValidity()
on an HTML element asking if it is valid. We can call this method from the DevTools console:
Anything we can do from the console, we can do from a Cypress test. Unfortunately, since Cypress returns jQuery element, we cannot simply invoke the method
1 | /// <reference types="cypress" /> |
Instead we must call the method on the original HTML element:
1 | /// <reference types="cypress" /> |
Hmm, not sure why the boolean is reported twice in the Command Log!
Tip: you can check form's validity too!
validationMessage
The error displayed by the browser is specific to the validation rule. We can retrieve it from the Console.
Let's confirm it by test:
1 | /// <reference types="cypress" /> |
validity
Checking the validation message only retrieves the single message shown to the user, what about all validation rules? Turns out we can retrieve the full validation status by looking up another property. For example, the quantity input element cannot have numbers below 1.
Let's test it.
1 | /// <reference types="cypress" /> |
Oops, does not work, even though the objects do look the same.
Turns out, the objects are different because the Element.validity()
returns ValidityState that is different from a plain object.
The simplest way to compare this object is to use deep.include
assertion.
1 | /// <reference types="cypress" /> |
Form is not submitted
Finally, let's confirm the form is not submitted until the inputs are valid. Let's test this - we will catch the form submission by attaching our own event handler from the test - after all, Cypress tests run right next to your application, so we can do anything we want.
1 | /// <reference types="cypress" /> |
When this test runs, the browser pops its error validation message, but does not submit the form.
Just for fun, let's confirm the test does fail if the browser submits the form
1 | // same test but let's enter quantity |
Our test catches it
We should change the test back, and add another test to make sure the form is submitted when there are no validation errors.
1 | it('is submitted when valid', () => { |
💡 You can also intercept the form submission using cy.intercept command, see the cy.intercept Recipe
Bonus 1: Match CSS class ":valid" and ":invalid"
You can also check if the input field is valid or invalid by matching the pseudo classes.
1 | // the input field is invalid |