ScreenshotNeo

BlogHow-to

How to Simulate Tab Key Presses in Cypress Tests

Use Cypress’s built-in `cy.press()` command to simulate Tab, then assert which element receives focus. Includes focus-order examples, version notes, and troubleshooting.

By the ScreenshotNeo team4 October 20264 min read

Use Cypress’s built-in cy.press(Cypress.Keyboard.Keys.TAB) to simulate pressing Tab. Start from a predictable focus target, issue the key press, then use a separate Cypress query to assert which element has focus:

it('moves focus to the next form field when pressing Tab', () => {
  cy.visit('/my-login')
  cy.get('input.email').focus()
  cy.press(Cypress.Keyboard.Keys.TAB)
  cy.get('input.password').should('have.focus')
})

cy.press() yields null, so put the focus assertion on a fresh query rather than chaining it directly to the key press. See the Cypress press API.

1. Choose the right Cypress command

Use cy.press() for navigation keys such as Tab, arrow keys, Enter, and Escape. Use cy.type() to enter text into an input or to send its supported text-oriented special sequences. Cypress recommends cy.press() for navigation keys, particularly when the key is used outside ordinary text entry. See the Cypress type API.

For Tab navigation, the built-in key constant is Cypress.Keyboard.Keys.TAB; the API’s corresponding key value is Tab. Cypress documents cy.press() as triggering native key events in the application, making it suitable for testing keyboard navigation and focus management.

2. Write a focused Tab test

A reliable test has three steps:

  1. Visit the page or render the component under test.
  2. Move focus to a known starting element.
  3. Press Tab and assert focus on the expected destination.
it('follows the login form tab order', () => {
  cy.visit('/my-login')

  cy.get('input.email').focus().should('have.focus')
  cy.press(Cypress.Keyboard.Keys.TAB)
  cy.get('input.password').should('have.focus')

  cy.press(Cypress.Keyboard.Keys.TAB)
  cy.get('button[type="submit"]').should('have.focus')
})

Keep each press and assertion as a separate step. This makes the intended focus order clear and gives Cypress a fresh element query for each assertion. Use selectors that identify the actual interactive controls in your application.

3. Check focus order and focus traps

For a longer form or navigation menu, repeat the press-and-assert pattern at each meaningful point. Test the behavior users rely on, such as the order through fields, links, and buttons, rather than asserting every incidental element on a complex page.

If a dialog or other region intentionally traps focus, test both entering the region and the expected behavior at its boundaries. The correct destination depends on the application’s design. Cypress’s accessibility testing guide provides context for testing accessibility behavior.

4. Check Cypress version compatibility

Cypress added cy.press() in version 14.3.0. If the command or key constant is unavailable, check the installed Cypress version and the current API documentation. Cypress announced expanded support for named keys and other characters in version 15.1.0; see the 15.1.0 announcement.

The Cypress plugin directory also lists community extensions such as cypress-plugin-tab and cypress-real-events. Consider them only if your project has a specific need the built-in command does not meet. The directory distinguishes community extensions from Cypress-maintained plugins; see the Cypress plugin directory.

5. Troubleshoot common problems

Symptom Likely cause What to do
cy.press is not recognized The project uses Cypress older than 14.3.0. Check the installed version and update if appropriate, or consult the current Cypress API for the version you use.
The focus assertion fails after Tab The starting element was not focused, or the page’s actual focus order differs from the expected order. Assert focus on the starting element first, then inspect the interactive elements and expected order in the application.
The assertion is chained from cy.press() cy.press() yields null, not the focused element. Run a fresh query for the expected destination and assert have.focus on it.
cy.type('{tab}') does not behave as intended cy.type() is intended for text entry and supported text-oriented sequences; Tab is a navigation key. Use cy.press(Cypress.Keyboard.Keys.TAB) and then query the expected focus destination.
Focus remains within a dialog The application may intentionally trap focus inside that region. Check the dialog’s intended boundary behavior and assert the expected destination for that design.

6. Performance, reliability, and cost

This is a direct Cypress keyboard interaction, so the main reliability concern is test setup: begin from a known focus target and assert the expected result after each navigation key. Keep the page state deterministic, especially when testing dialogs or menus whose focus behavior depends on whether they are open.

The method uses Cypress’s built-in command and requires no separate Tab plugin for the documented use case. The research sources provide no relevant performance benchmarks or cost figures for this command.

7. FAQ

Can I use .type('{tab}')?

For keyboard navigation, use cy.press(Cypress.Keyboard.Keys.TAB). Then query the element expected to receive focus.

Does cy.press() return the focused element?

No. It yields null; assert focus with a separate Cypress query.

What should a Tab-order test verify?

Verify the expected focus destination from a known starting point, including intentional focus-trap behavior where the design calls for it.

Or skip the browser setup

If your goal is to inspect a page visually while building or debugging a test, ScreenshotNeo can return a screenshot with one GET request. It is a website screenshot API and MCP server for developers; see ScreenshotNeo and the API documentation.

curl -G "https://api.screenshotneo.com/v1/shot" -d access_key=YOUR_API_KEY --data-urlencode url=https://stripe.com -o shot.webp
import requests
r = requests.get("https://api.screenshotneo.com/v1/shot", params={"access_key": "YOUR_API_KEY", "url": "https://stripe.com"}, timeout=90)
open("shot.webp", "wb").write(r.content)
const q = new URLSearchParams({ access_key: 'YOUR_API_KEY', url: 'https://stripe.com' });
const res = await fetch(`https://api.screenshotneo.com/v1/shot?${q}`);
  • Cookie banners, popups, and chat widgets are removed before the shot; each cleanup step can be turned off.
  • Bot checks, blank pages, timeouts, failed loads, and cache hits cost nothing. Responses identify the page verdict and billing status in headers.
  • An MCP server lets AI agents, including Claude and Cursor, take screenshots with ScreenshotNeo tools.
  • The free plan includes 1,000 screenshots a month with no card; paid plans start at $5 for 3,000.

Sign up for 1,000 free screenshots a month, with no card.