My Navbar component relies on the useRouter
function provided by nextjs/router
in order to style the active links.
I'm trying to test this behavior using Cypress, but I'm unsure of how I'm supposed to organize it. Cypress doesn't seem to like getRoutePathname()
and undefined is returned while within my testing environment.
Here's the component I'm trying to test:
import Link from 'next/link'
import { useRouter } from 'next/router'
function getRoutePathname() {
const router = useRouter()
return router.pathname
}
const Navbar = props => {
const pathname = getRoutePathname()
return (
<nav>
<div className="mr-auto">
<h1>Cody Bontecou</h1>
</div>
{props.links.map(link => (
<Link key={link.to} href={link.to}>
<a
className={`border-transparent border-b-2 hover:border-blue-ninja
${pathname === link.to ? 'border-blue-ninja' : ''}`}
>
{link.text}
</a>
</Link>
))}
</nav>
)
}
export default Navbar
I have the skeleton setup for the Cypress component test runner and have been able to get the component to load when I hardcode pathname
, but once I rely on useRouter
, the test runner is no longer happy.
import { mount } from '@cypress/react'
import Navbar from '../../component/Navbar'
const LINKS = [
{ text: 'Home', to: '/' },
{ text: 'About', to: '/about' },
]
describe('<Navbar />', () => {
it('displays links', () => {
mount(<Navbar links={LINKS} />)
})
})
Since the original posting Cypress added some better documentation on component testing NextJs.
Specifically on the router Customize your Next.js Testing Experience this is the example (simplified)
If you add it to the custom nextMountWithStubbedRoutes()
command, it can be used in any spec.
Cypress.Commands.add('nextMountWithStubbedRoutes', (component, options) => {
const router = {
route: '/',
pathname: '/',
query: {},
asPath: '/',
basePath: '',
back: cy.stub().as('router:back'),
forward: cy.stub().as('router:forward'),
push: cy.stub().as('router:push'),
reload: cy.stub().as('router:reload'),
replace: cy.stub().as('router:replace'),
isReady: true,
...(options?.router || {}),
}
return mount(
<RouterContext.Provider value={router}>
{component}
</RouterContext.Provider>,
options
)
})
See caveat:
Unfortunately, there’s no such thing as a free lunch—adding these extra items to every mount will affect performance and introduce global state elements outside the bounds of your component. It’s up to you to decide whether these trade-offs are worth it based on your use case.
Given this warning, use specific nextMountWithStubbedRoutes()
only with tests that need it.