Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Routing broken (documented use case fails) #869

Closed
blerchin opened this issue Jan 28, 2021 · 4 comments
Closed

Routing broken (documented use case fails) #869

blerchin opened this issue Jan 28, 2021 · 4 comments

Comments

@blerchin
Copy link

  • @testing-library/react version: 11.2.3
  • Testing Framework and version: jest 26.6.3
  • DOM Environment: jsdom 16.4.0

Relevant code or config:

This is directly copypasta'd from:
https://testing-library.com/docs/example-react-router/

// app.test.js
import {render, screen} from '@testing-library/react'
import userEvent from '@testing-library/user-event'
import {createMemoryHistory} from 'history'
import React from 'react'
import {Router} from 'react-router-dom'

import '@testing-library/jest-dom/extend-expect'

import {Link, Route, Switch, useLocation} from 'react-router-dom'

const About = () => <div>You are on the about page</div>
const Home = () => <div>You are home</div>
const NoMatch = () => <div>No match</div>

export const LocationDisplay = () => {
  const location = useLocation()

  return <div data-testid="location-display">{location.pathname}</div>
}

export const App = () => (
  <div>
    <Link to="/">Home</Link>

    <Link to="/about">About</Link>

    <Switch>
      <Route exact path="/">
        <Home />
      </Route>

      <Route path="/about">
        <About />
      </Route>

      <Route>
        <NoMatch />
      </Route>
    </Switch>

    <LocationDisplay />
  </div>
)

test('full app rendering/navigating', () => {
  const history = createMemoryHistory()
  render(
    <Router history={history}>
      <App />
    </Router>,
  )
  // verify page content for expected route
  // often you'd use a data-testid or role query, but this is also possible
  expect(screen.getByText(/you are home/i)).toBeInTheDocument()

  const leftClick = {button: 0}
  userEvent.click(screen.getByText(/about/i), leftClick)

  // check that the content changed to the new page
  expect(screen.getByText(/you are on the about page/i)).toBeInTheDocument()
})

test('landing on a bad page', () => {
  const history = createMemoryHistory()
  history.push('/some/bad/route')
  render(
    <Router history={history}>
      <App />
    </Router>,
  )

  expect(screen.getByText(/no match/i)).toBeInTheDocument()
})

test('rendering a component that uses useLocation', () => {
  const history = createMemoryHistory()
  const route = '/some-route'
  history.push(route)
  render(
    <Router history={history}>
      <LocationDisplay />
    </Router>,
  )

  expect(screen.getByTestId('location-display')).toHaveTextContent(route)
})

What you did:

Using React Testing Library to test react-router rendering after route change. Per docs, I provide a Router context with my own history object from createMemoryHistory.
After changing the route by clicking a link with userEvent I expect the corresponding Route to be rendered.

What happened:

The route change is reflected by the original page
subscribing components (eg. location retrieved by useLocation). However code inside the newly-matching route is not rendered. In this use case, the "You are on the about page" text never appears.

Unable to find an element with the text: /you are on the about page/i. This could be because the text is broken up by multiple elements. In this case, you can provide a function for your text matcher to make your matcher more flexible.

<body>
  <div>
    <div>
      <a
        href="/"
      >
        Home
      </a>
      <a
        href="/about"
      >
        About
      </a>
      <div>
        No match
      </div>
      <div
        data-testid="location-display"
      />
    </div>
  </div>
</body>

Reproduction:

https://codesandbox.io/s/musing-http-rohnt?file=/src/docs.test.js

Problem description:

Cannot test full-app routing using testing library. Examples on the documentation site appear to be broken.

Suggested solution:

None at this point, but willing to work on a fix if someone can isolate the issue.

@blerchin
Copy link
Author

blerchin commented Jan 28, 2021

I have dug into this issue further and it may not be specifically related to RTL. Probably a jest-dom issue.

For reasons I don't entirely understand, history.push('/mypath') is appending a location of shape

{
  action: 'PUSH',
  location: {
      pathname: '/mypath',
      search: '',
      hash: '',
      state: null,
      key: 'hb7yr0f5'
  }
}

React router expects to see a location as follows:

 {
      pathname: '/mypath',
      search: '',
      hash: '',
      state: null,
      key: 'hb7yr0f5'
  }

The malformed location is picked up by <Switch> and it does not match because location.pathname is undefined.

This happens when it is called from inside a component, using the context-provided history. When the history object (directly from createMemoryHistory) is used, the location is pushed as expected by react-router and the routing succeeds. Will continue to investigate. Would appreciate any leads if anyone has deep experience with react-router and knows if there was perhaps a breaking change introduced.

@alexkrolick
Copy link
Collaborator

alexkrolick commented Jan 28, 2021

Looks like a difference between the history package which provides the RR history object (including in memory versions) and the browser's window.history global.

I think if you are using the history prop, you need to use an InMemoryRouter and not the BrowserRouter that syncs with the browser's history/location objects.

@blerchin
Copy link
Author

@alexkrolick you are sort of correct that it was a difference between the history package and what RR was expecting. Just figured it out and the issue was that when I imported createMemoryHistory I was using history@5.0.0. However react-router still depends on ^4.9.0 and loads that internally.

I am not entirely sure why that first call to history.push worked but the subsequent <Link did not. But can confirm that changing the dependency for history to ^4.9.0 fixed the issue.

@blerchin
Copy link
Author

Not sure if something should be added to the docs to warn against this issue. Closing this ticket for now because it's not an issue with RTL.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants