Keyboard testing helper functions
Cypress natively does not handle keyboard tests in a way that always serves our needs. Luckily, there is a library that is able to fix some of our issues called cypress-real-events. This allows us to implement a more standard response to keyboard-driven behavior in Cypress, which allows us to test both typing functionality and accessibility functionality related to using a keyboard for interactivity.
Keypress commands
cy.realPress(key)
This is a native function to the cypress-real-events
library. Use this function to trigger a single key press in your test.
cy.realPress('Tab');
This function can also accept an array with multiple keys together. So for example if you wanted to navigate backwards:
cy.realPress(['Shift', 'Tab'])
cy.keys(array)
This is a custom function that will loop multiple cy.realPress
commands. It takes an array of args that can either be single strings of one key, or arrays of multiple keys:
cy.keys(['Tab', 'Enter']);
cy.keys([['Shift', Tab], 'Enter']);
cy.repeatKey(key, multiple)
This custom function will allow us to repeat a keypress to avoid repetitive calls to cy.realPress
or cy.keys
:
cy.repeatKey('Tab', 3);
cy.repeatKey(['Shift', 'Tab'], 2);
Accessibility Keyboard Commands
cy.allyEvaluateSelectMenu(selectMenu, optionText, selectedOption)
This custom function checks multiple things for accessibility regarding the select menu:
It verifies the select menu is visible and focused
With the select focused, it uses
cy.keys
to type the string passed inoptionText
.It verifies that the selected option contains the text of what was passed into the function on
selectedOption
.
cy.allyEvaluateSelectMenu('#militaryStatus', 'child', 'Child');
cy.allyEvaluateRadioButtons(selectorArray, arrowPressed, reversed = false)
This custom function verifies that once focused, a group of radio buttons can be iterated through with arrow keys. The arrowPressed
argument allows you to choose which direction you want to travel, and the reversed
option gives you the option to start at the end of your array of elements instead of the beginning:
cy.allyEvaluateRadioButtons(
[
'input#root_onBehalfOf_0',
'input#root_onBehalfOf_1',
'input#root_onBehalfOf_2',
],
'ArrowDown',
);
// Reversed
cy.allyEvaluateRadioButtons(
[
'input#root_onBehalfOf_0',
'input#root_onBehalfOf_1',
'input#root_onBehalfOf_2',
],
'ArrowUp',
true,
);
cy.allyEvaluateInput(input, inputText)
This custom function is designed to type a value into a focused input and then verify that the input holds that value appropriately.
cy.allyEvaluateInput('#root_fullName_first', 'Benjamin');
cy.allyEvaluateCheckboxes(selectorArray)
This custom function takes a collection of checkboxes and attempts to check them all off by tabbing through them and hitting Space
on each of them. It then verifies that they are checked off appropriately before moving to the next checkbox.
cy.allyEvaluateCheckboxes(['input[type="checkbox"]']);
cy.allyEvaluateModalWindow(modalTrigger, modalElement, modalCloseElement, triggerKey = ‘Enter’)
This custom function takes a number of parameters that are defined as follows:
modalTrigger
: This is the selector for what will be interacted with to activate the modal opening.
modalElement
: This is the selector for the actual modal element
modalCloseElement
: This is the button inside the modal that tells the modal to close
triggerKey
: This is an optional parameter that allows you to set which key will be used to open and close the modal. The default is Enter
.
This function goes through the keyboard-driven process of activating the modal opening, verifying that the modal opened and then closing it, verifying that the focus returns to the trigger to open it again on closing.
cy.allyEvaluateModalWindow(
'button[aria-label="Learn more about how the length of Montgomery GI Bill active-duty service affects your benefits"]',
'div[role="alertdialog"]',
'button[aria-label="Close this modal"]',
);
cy.hasFocusableCount(selector, count)
This custom function takes a selected element and verifies how many focusable elements exist inside of it.
cy.hasFocusableCount('div.usa-width-two-thirds form', 14);
cy.hasTabbableCount(selector, count)
This custom function takes a selected element and verifies how many tabbable elements exist inside of it.
cy.hasTabbableCount('div.usa-width-two-thirds form', 11);
Form Testing Helpers
There are also additional form helper functions that utilize the realPress
function described above. These allow tests that can navigate and fill out the form through real keyboard events.
timeoutDuration
This is the time delay between each keyboard action when using the following functions. This presents a more real-life scenario than doing each action instantly.
let timeoutDuration = 100;
cy.tabToElement(selelctor, forward)
This is a custom function that will loop multiple cy.realPress(tab)
commands to navigate the users focus to the element that matches the selector passed in. It also takes a Boolean to tab forward or backward (default forward).
cy.tabToElem('#next-page-button'); // tabs forward to button
cy.tabToElem(#breadcrumb-option=1', false); // tabs backwards to a breadcrumb option
cy.chooseSelectOptionByTyping(value)
This is a custom function that selects an option from a focused select box. Also works with select options with an empty string for a value.
cy.chooseSelectOptionByTyping('june'); // opens, navigates to, and selects the month of June
Note: Ideally, this would use the arrow keys to go through the options and select one but with the current tools using the arrow keys within a select does not work on Mac devices. This typing method is a workaround until that is fixed in the libraries used.
cy.chooseRadio(value)
This is a custom function that uses various button presses to select a radio option when a radio option is focused. It will navigate through the radio options with the same name and select the one with the matching value.
cy.chooseRadio('hospital-3'); // navigates to, and selects hospital 3 of a radio group
cy.typeInFocused(value)
This is a custom function that will type in a focused text field or text area. Similar to the cy.type
function but will have a consistent delay between keystrokes to match the delay between all of the other keyboard actions
cy.typeInFocused('First Name Last Name'); // types each letter one at a time
Help and feedback
Get help from the Platform Support Team in Slack.
Submit a feature idea to the Platform.