What’s actually slowing this PC down?
Pick the symptom - the matching free tool is one click away.
To click a submenu item in Cypress, open or reveal the menu the way your application does, query the item with a stable selector scoped to that menu, click it, and assert the resulting URL or UI state. For click-to-open menus, use a normal .click(). For JavaScript-driven hover behavior, use .trigger('mouseover'); Cypress has no built-in cy.hover(), and a synthetic event cannot activate CSS-only :hover rules.
The reliable pattern for a click-open submenu
Model the user journey in separate commands: open the parent menu, find the child link inside that menu, click it, then query the destination afresh. A dedicated application-owned attribute such as data-cy is usually more stable than a class name or a long CSS path.
cy.get('[data-cy="menu-toggle"]').click()
cy.get('[data-cy="products-menu"]')
.should('be.visible')
.contains('a', 'Analytics')
.click()
cy.location('pathname').should('eq', '/products/analytics')
The names and route above are examples; replace them with the attributes, label and URL used by your application. Scoping the text query to [data-cy="products-menu"] prevents a second “Analytics” link elsewhere on the page from being selected.
Why query the result again?
A click can navigate, rerender the navigation tree, or remove the menu from the DOM. Cypress warns that chaining assertions from the old subject after .click() can be unsafe when the click changes the page. Start a new query for the expected result:
Quick wins for a faster PC:
Clear out junk files and repair common Windows errorsFree Scan →Fix the driver behind crashes, sound loss and screen glitchesFind Drivers →#1 Best Overall
cy.get('[data-cy="products-menu"]')
.contains('a', 'Analytics')
.click()
cy.get('[data-cy="analytics-heading"]')
.should('be.visible')
Choosing selectors that survive UI changes
Prefer dedicated data attributes
Ask the application team to add attributes intended for tests, for example:
<button data-cy="menu-toggle">Products</button>
<nav data-cy="products-menu">
<a data-cy="analytics-link" href="/products/analytics">Analytics</a>
</nav>
Then the test can use either the item’s own attribute or a scoped text query:
cy.get('[data-cy="menu-toggle"]').click()
cy.get('[data-cy="analytics-link"]').click()
Handle duplicate matches deliberately
A click normally targets one DOM element. If a query matches several elements, narrow it rather than clicking every match:
- Scope with a menu container:
cy.get('[data-cy="products-menu"]').contains('a', 'Analytics'). - Use an exact, application-owned attribute for a unique item.
- Use
.first()or.eq(index)only when the ordering is part of the UI contract and is documented in the test.
A hidden mobile menu and a visible desktop menu can create duplicate labels. Scoping to the currently opened container is safer than blindly choosing the first match.
Hover-dependent submenus
First identify what actually reveals the submenu. Some applications listen for a JavaScript mouseover event; others rely solely on the browser’s CSS :hover state.
Rank #2
When JavaScript handles mouseover
For an event handler that responds to mouseover, Cypress’s documented workaround is .trigger('mouseover'):
cy.get('[data-cy="products-item"]')
.trigger('mouseover')
cy.get('[data-cy="products-menu"]')
.should('be.visible')
.contains('a', 'Analytics')
.click()
cy.location('pathname').should('eq', '/products/analytics')
Waiting for visibility before clicking makes the intermediate state explicit and lets Cypress retry the assertion while the application opens the menu.
When CSS :hover reveals the submenu
trigger('mouseover') dispatches a JavaScript event; it does not move a real pointer and therefore does not create CSS hover effects. A CSS-only submenu may remain hidden even though the event was triggered. Cypress documentation states that there is no built-in cy.hover() command. If genuine pointer movement is required, use a real-event solution such as the cypress-real-events plugin, after confirming that its current support matches your Cypress version and browser setup.
Keep the test focused on the behavior your product promises. If the requirement is “a pointer moving over this item opens a CSS submenu,” a real pointer event is the faithful test. If the requirement is an application JavaScript handler, trigger('mouseover') is sufficient and faster.
Keyboard-accessible alternatives
Many menus support keyboard navigation as well as hover. If the product specification says the submenu opens on focus or a key, test that contract directly instead of simulating hover:
Rank #3
cy.get('[data-cy="products-item"]').focus()
cy.get('[data-cy="products-menu"]').should('be.visible')
cy.get('[data-cy="products-menu"]').contains('a', 'Analytics').click()
Use the event or interaction that real users are expected to use; do not add a forced click merely because a menu is difficult to reach.
Actionability, retries, and timing
A normal .click() waits for Cypress’s actionability checks. These include whether the element is visible, covered, disabled or still moving. While waiting, Cypress can re-run the queries that produced the element. The click itself occurs once when the target becomes actionable; assertions after it are the parts that retry.
Free tools Windows power users keep installed
One-click scans. No signup required.
For a menu with an animation, assert the opened state before clicking:
cy.get('[data-cy="menu-toggle"]').click()
cy.get('[data-cy="products-menu"]')
.should('be.visible')
.and('not.have.class', 'is-opening')
cy.get('[data-cy="products-menu"]').contains('a', 'Analytics').click()
Avoid arbitrary sleeps such as cy.wait(2000) when a meaningful state, selector or network condition can express readiness. A state assertion documents what the test needs and finishes as soon as that state exists.
When (and when not) to use force: true
click({ force: true }) bypasses actionability checks. It can fire an event on an element that is hidden, covered by an overlay, outside the viewport or still transitioning. That makes it an escape hatch, not a normal submenu technique.
Rank #4
cy.get('[data-cy="analytics-link"]')
.click({ force: true })
Use force only when the test intentionally verifies an event despite presentation—for example, a lower-level component contract where visibility is tested separately—and leave a comment explaining why. In an end-to-end navigation test, a forced click can make a broken menu pass while real users cannot reach the link.
The Tool Desk
Outbyte PC Repair FREEClear out junk files and repair common Windows errorsFree Scan →Outbyte Driver Updater FREEFix the driver behind crashes, sound loss and screen glitchesFind Drivers →Diagnosing a failed submenu click
“Element not found”
- Confirm the parent menu was opened before querying its child.
- Check spelling, case and whitespace in the visible label.
- Inspect the rendered DOM for a portal: the submenu may be mounted outside the parent element, so scope to the actual submenu container instead.
- Check responsive conditions; the test viewport may render a different navigation.
“Element is not visible”
- For a click-open menu, assert the menu’s visible state after opening.
- For a hover menu, determine whether the reveal is JavaScript or CSS. A synthetic event cannot activate CSS-only hover.
- Look for a hidden duplicate matching the same text and narrow the selector to the visible menu.
“Element is covered” or “not receiving pointer events”
- Identify cookie notices, dialogs, sticky headers or loading masks covering the item.
- Close the overlay through the same control a user would use, or wait for the application’s loaded state.
- Check whether an animation has finished before clicking.
- Do not jump straight to
force: true; coverage often indicates a real usability defect.
Timeout after trigger(‘mouseover’)
- Verify that the application listens for
mouseover, not onlymouseenteror a pointer event. - If the reveal is CSS-driven, switch to a real pointer-event approach.
- Ensure the event is triggered on the element that owns the handler, not a child icon inside it.
Click succeeds but the route assertion fails
- Use the application’s actual destination, including a trailing slash, hash or locale prefix where applicable.
- Assert the pathname, query string or a destination heading according to the contract; do not assume every navigation is a full page load.
- Query the destination after the click rather than asserting against the stale menu subject.
Complete examples for common menu implementations
Button opens an inline submenu
describe('Products submenu', () => {
it('opens and navigates to Analytics', () => {
cy.visit('/dashboard')
cy.get('[data-cy="menu-toggle"]').click()
cy.get('[data-cy="products-menu"]').should('be.visible')
cy.get('[data-cy="products-menu"]')
.contains('a', 'Analytics')
.click()
cy.location('pathname').should('eq', '/products/analytics')
})
})
JavaScript mouseover reveal
it('navigates through the mouseover submenu', () => {
cy.visit('/dashboard')
cy.get('[data-cy="products-item"]').trigger('mouseover')
cy.get('[data-cy="products-menu"]').should('be.visible')
cy.get('[data-cy="products-menu"]')
.contains('a', 'Analytics')
.click()
cy.location('pathname').should('eq', '/products/analytics')
})
Multiple menus with the same child label
cy.get('[data-cy="admin-menu"]').within(() => {
cy.contains('a', 'Settings').click()
})
cy.location('pathname').should('eq', '/admin/settings')
.within() keeps every subsequent query inside the selected menu, which is useful when several navigation groups contain identically named links.
Performance and reliability practices
- Use short, stable selectors rather than deeply nested CSS chains.
- Open one menu per test path and assert the opened state once.
- Prefer deterministic state assertions to fixed delays.
- Keep route assertions specific enough to catch a wrong destination, but do not couple them to unrelated rendering details.
- Separate accessibility or keyboard tests from pointer-specific tests so a failure identifies the interaction that regressed.
- Capture diagnostic screenshots or videos from your Cypress runner when an intermittent overlay or animation is suspected.
Or skip the browser setup
If your goal is a clean image of a menu state or page rather than an interaction assertion, ScreenshotNeo provides a single website-screenshot API call. It can accept consent banners before capture and remove more than 60 known consent platforms, newsletter popups and chat widgets; each cleanup step can be disabled. Bot checks or CAPTCHAs, blank pages, timeouts, failed loads and cache hits are not billed, and the response identifies the result with X-Page-Verdict and X-Billed headers.
For an ordinary page capture, use the documented API parameters (the API also accepts the parameter names used by other screenshot services):
curl -G "https://api.screenshotneo.com/v1/shot"
-d access_key=YOUR_API_KEY
--data-urlencode url=https://stripe.com
-o shot.webp
Python:
import requests
r = requests.get(
"https://api.screenshotneo.com/v1/shot",
params={"access_key": "YOUR_API_KEY", "url": "https://stripe.com"},
timeout=90,
)
r.raise_for_status()
open("shot.webp", "wb").write(r.content)
Node.js:
const q = new URLSearchParams({
access_key: 'YOUR_API_KEY',
url: 'https://stripe.com'
});
const res = await fetch(`https://api.screenshotneo.com/v1/shot?${q}`);
if (!res.ok) throw new Error(`HTTP ${res.status}`);
const buffer = Buffer.from(await res.arrayBuffer());
await require('fs').promises.writeFile('shot.webp', buffer);
See the complete parameter reference and response details in the ScreenshotNeo documentation. For submenu-specific visual states, you can combine a custom script or click option with waits, selector targeting and hidden elements; the service also supports full-page captures with lazy images, CSS-selector element captures, dark mode, device presets, arbitrary viewports, retina scale, custom CSS and JavaScript, request blocking, cookies, headers, user agents, authorization, timezone and geolocation. PDF output, HTML/CSS-to-image, signed links, asynchronous jobs with signed webhooks, bulk capture of up to 100 URLs per call, caching with a chosen TTL, a usage API and an OpenAPI specification are available on every plan.
Outdated Drivers Are Slowing You Down
One free scan finds every outdated or missing driver and matches the right update for your exact hardware.Free scan · exact hardware matchWindows Errors? Fix Them Before They Spread
Repair common Windows errors and clear accumulated junk for a smoother, more stable PC - no reinstall needed.Free scan · no reinstallScreenshotNeo includes an MCP server with take_screenshot, get_page_info and capture_pdf tools for Claude, Cursor and other MCP clients. The Free plan includes 1,000 shots each month without a card; paid plans start at $5 for 3,000 shots. Create a free ScreenshotNeo account to try it.
FAQ
Can I use cy.contains() without cy.get()?
Yes. cy.contains('a', 'Analytics') can locate a link directly, but scoping it to the opened submenu is safer when labels repeat.
Does trigger(‘mouseover’) move the mouse?
No. It dispatches a JavaScript event and does not apply CSS :hover effects or emulate physical pointer movement.
Should every submenu test use force: true?
No. A normal click checks that the item is user-actionable. Force is appropriate only for a deliberate, separately justified exception.
What should I assert after clicking?
Assert the contract that matters: a pathname, query string, destination heading, selected state or another newly queried result. Choose the assertion that distinguishes the intended submenu destination from nearby links.
Quick Recap
Product prices and availability are accurate as of the date/time indicated and are subject to change. Any price and availability information displayed on Amazon at the time of purchase will apply.




