testing routing capabilities of preact-router

914 Views Asked by At

How would I be able to test the router in the code below? When using React you are able to use MemoryRouter to pass initialEntries to mock a route change but I cannot find an alternative for preact-router. I looked at the Preact docs and the preact-router docs but I am unable to find a clear solution.

import 'preact/debug';
import { h, render } from 'preact';
import HomePage from './pages/homepage';
import Router from 'preact-router';
import AsyncRoute from 'preact-async-route';
import './styles/index.scss';

const App = () => (
  <Router>
    <HomePage path="/" />
    <AsyncRoute
      path="/admin"
      getComponent={ () => import('./pages/admin').then(module => module.default) }
    />
  </Router>
);

export default App;
1

There are 1 best solutions below

0
On

This is a little old, but I figured I would share what I found.

The first and quickest thing to do is to just use the route function in preact-router.

import { render, route } from 'preact-router';

import App from './App';

describe('<App/>', () => {
    it('renders admin', async () => {
        const { container, findByText } = render(<App/>);
        // Go to admin page
        route('/admin');
        // Wait for page to load since it's loaded async
        await findByText(/Admin Page/);
        // perform expectations.
    });
});

While this works, I don't like that it relies on the brower's real history. Luckily, the <Router> component accepts a history prop of type CustomHistory. So you can use an in-memory implementation of a History API to make this happen. I think I've seen docs that suggest using the history package - however I had to make an adjustment

import { createMemoryHistory } from 'history';

class MemoryCustomHistory {
    constructor(initialEntries = undefined) {
      this.wrapped = createMemoryHistory({initialEntries});
    }
    get location() {
        return this.wrapped.location;
    }
    // Listen APIs not quite compatible out of the box.
    listen(callback) {
        return this.wrapped.listen((locState) => callback(locState.location));
    }
    push(path) {
        this.wrapped.push(path);
    }
    replace(path) {
        this.wrapped.replace(path);
    }
}

Next, update your app to accept a history property to pass to the <Router>

const App = ({history = undefined} = {}) => (
  <Router history={history}>
    <HomePage path="/" />
    <AsyncRoute
      path="/admin"
      getComponent={ () => import('./pages/admin').then(module => module.default) }
    />
  </Router>
);

Finally, just update the tests to wire your custom history to the app.

  it('renders admin', async () => {
    const history = new MemoryCustomHistory(['/admin]);
    const { container, findByText } = render(<App history={history}/>);
    // Wait for page to load since it's loaded async
    await findByText(/Admin Page/);
    // perform expectations.
  });