Jest enzyme test router with props - jestjs

I have react application and started covering it with unit tests.
My app file contains:
export default class App extends Component {
render() {
return (
<Provider store={store}>
<Router history={history}>
<div>
<Header />
<Switch>
<Route exact path="/" component={Home} />
<Route exact path="/detail/:collectionName/:RecordId/:firmId" component={DetailDialog} />
<Route exact path="/dashboard" component={Dashboard} />
<Route path="/auth-login" component={LogIn} />
<Route path="/auth-register" component={Register} />
<Route path="/user-manager" component={this.props.decoded.role==='Admin' ? Admin : Home} />
</Switch>
</div>
</Router>
</Provider>
);
}
}
I want to make next test: go to login, check href and by href check component.
it.only('test right component is attached', () => {
const wrapper = shallow(<Login loginUser={fn}/>);
const registerLink = wrapper.find("[data-qa='sign-up']").props().href;
const history = jest.fn();
const props = {
decoded: {role: 'Admin'}
}
const router = mount(
<MemoryRouter initialEntries={[registerLink]}>
<App history={history} {...props} />
</MemoryRouter>
);
expect(router.find(Register)).toHaveLength(1);
});
Until mounting everything is ok. But on mount I receive the next error:
console.error
Error: Uncaught [Error: Unable to find node on an unmounted component.]
What am I doing wrong? How can I mount the component and check that Register is inside?

Try to pass a history prop not as a jest.fn(), but as a { push : jest.fn() }

Related

Uncaught Error: A <Route> is only ever to be used as the child of <Routes> element, never rendered directly. Please wrap your <Route> in a <Routes>

After implemented Private Route I keep getting this error.
The problem appears when Im trying to get to the Profile page that nested in the .
App.js
import './App.css';
import { BrowserRouter, Route, Routes } from 'react-router-dom';
import LoginPage from './pages/LoginPage';
import SignUpPage from './pages/SignUpPage';
import ProfilePage from './pages/ProfilePage';
import HomePage from './pages/HomePage';
import AboutPage from './pages/AboutPage';
import AdditionalItemPage from './pages/AdditionalItemPage';
import ReturnOfVehiclePage from './pages/ReturnOfVehiclePage';
import PolicyPage from './pages/PolicyPage';
import CarPage from './pages/CarPage';
import CarListPage from './pages/CarsListPage';
import NavBar from './NavBar';
import Footer from './Footer';
import NotFoundPage from './pages/NotFoundPage';
import { PrivateRoute } from './auth/PrivateRoute';
function App() {
return (
<BrowserRouter>
<div className="App">
<NavBar />
<div>
<Routes>
<Route path="/" element={<HomePage />} />
<Route path="/login" element={<LoginPage />} />
<Route path="/signup" element={<SignUpPage />} />
<Route path="/about" element={<AboutPage />} />
<Route path="/cars" element={<CarListPage />} />
<Route path="/cars/:carId" element={<CarPage />} />
<Route path="/additional-item" element={<AdditionalItemPage />} />
<Route path="/return-vehicle" element={<ReturnOfVehiclePage />} />
<Route path="/policy" element={<PolicyPage />} />
<Route
path="/profile"
element={
<PrivateRoute>
<ProfilePage />
</PrivateRoute>
}
/>
<Route path="*" element={<NotFoundPage />} />
</Routes>
</div>
<Footer />
</div>
</BrowserRouter>
);
}
export default App;
PrivateRoute.js comp
import { Route, Navigate } from 'react-router-dom';
import { useUser } from './useUser';
export const PrivateRoute = (props) => {
const user = useUser();
if (!user) return <Navigate to="/login" />;
return <Route {...props} />;
};
I've already tried to wrap everyting I though of in the
The actual Profile paga into Routes
The PrivteRoute into Routes
The Route into another Routes
Creating another Routes, separeted with the 1st one.
If remove PrivateRoute, everything works fine but it s not the solution I'm looking for.
You just didn't implement it properly , just change your PrivateRoute.js component like this :
import { Navigate } from 'react-router-dom';
import { useUser } from './useUser';
function PrivateRoute({ children }) {
const user = useUser();
return user ? children : <Navigate to="/login" />;
}
The error message is accurate in that a Route must be directly placed within a Routes wrap. To get around this, simply create a 2nd top-level Routes wrap and move the Route logic out of your PrivateRoute.tsx file.
Consider this example sandbox I created with some slight modifications to your code. You can switch user to false/true to see how the /profile private route reacts as expected.
Here's the bare-bones implementation:
App.tsx
import { BrowserRouter, Route, Routes } from "react-router-dom";
import PrivateRoutes from "./PrivateRoutes";
function App() {
return (
<BrowserRouter>
<div className="App">
<div>
<Routes>
<Route path="/" element={<div>Hello World</div>} />
</Routes>
<PrivateRoutes /> {/* <---- all private routes ---- */}
</div>
</div>
</BrowserRouter>
);
}
export default App;
PrivateRoutes.tsx
import { Route, Routes } from "react-router-dom";
import PrivateRoute from "./PrivateRoute";
export default function PrivateRoutes() {
return (
<Routes>
<Route
path="/profile"
element={
<PrivateRoute>
<div>Private Profile</div>
</PrivateRoute>
}
/>
</Routes>
);
}
PrivateRoute.tsx
import { ReactNode } from "react";
import { Navigate } from "react-router-dom";
interface PropsI {
children: ReactNode;
}
export default function PrivateRoute({ children }: PropsI) {
const user = false;
if (!user) return <Navigate to="/fail" />;
return <>{children}</>;
}

How can you use multiple outlets in react-router 6?

For example, say I have a Header and Content sub components that I would like to render on 1 page (single route path).
const App = () => {
<div>
<header><Outlet /></header>
<content><Outlet /><content>
</div>
}
const RouteConfig = () => {
return (
<Routes>
<Route path="/" element={<App />} />
<Route path="/app" element={<Header />} />
<Route path="/app" element={<Content /> />
</Route>
</Routes>
)
}
The above outputs the header contents for both Outlets, instead of the header and the contents. Is there a way to have multipe outlets?

how to set condition in react router DOM version 6?

I want to convert this code which has been written in react router v5 to v6 but I don't know how to do it, in general I want to set if the user doesn't have an account, redirect them to registration page an so on.
I'm aware of switch change and redirect the only problem is in this line of code :
<Route exact path="/">
{user ? <Home /> : <Redirect to="/register" />}
</Route>
the whole code:
import "./app.scss";
import Home from "./pages/home/Home";
import Register from "./pages/register/Register";
import Watch from "./pages/watch/Watch";
import Login from "./pages/login/Login";
import {
BrowserRouter as Router,
Switch,
Route,
Redirect,
} from "react-router-dom";
import { useContext } from "react";
import { AuthContext } from "./authContext/AuthContext";
const App = () => {
const { user } = useContext(AuthContext);
return (
<Router>
<Switch>
<Route exact path="/">
{user ? <Home /> : <Redirect to="/register" />}
</Route>
<Route path="/register">
{!user ? <Register /> : <Redirect to="/" />}
</Route>
<Route path="/login">{!user ? <Login /> : <Redirect to="/" />}</Route>
{user && (
<>
<Route path="/movies">
<Home type="movie" />
</Route>
<Route path="/series">
<Home type="series" />
</Route>
<Route path="/watch">
<Watch />
</Route>
</>
)}
</Switch>
</Router>
);
};
export default App;
You can use the element prop of v6, you don't need to use an exact prop on <Route path="/"> anymore. This is because all paths match exactly by default. If you want to match more of the URL because you have child routes use a trailing * as <Route path="/*" />.
import { BrowserRouter, Navigate, Route, Routes } from 'react-router-dom';
.
.
.
return (
<BrowserRouter>
<Routes>
<Route path="/" element={user ? <Home /> : <Navigate to="/register" replace />} />
.
.
.
</Routes>
</BrowserRouter>
)
.
.
.
Also you can see more detail in reactrouter docs:
https://reactrouter.com/docs/en/v6/getting-started/overview

ReactJS production with subfolder, navigation not working properly

I'm struggling to make my ReactJS production work in a subfolder of my website.
The dashboard works, when I go to http://xx.xx.xx.xx/dashboard it loads, but when I go to
http://xx.xx.xx.xx/dashboard/login I get a 404 not found page. When I host in the main folder it works perfectly.
As far as I know to make it work I have to set the basename of the subfolder in the Router of index.js,
which I did. Am I missing anything? I used "npm run build" to make the production.
index.js
ReactDOM.render(
<MuiThemeProvider theme={getMuiTheme()}>
<Provider store={store}>
<Router basename={"/dashboard"} history={history}>
<App />
</Router>
</Provider>
</MuiThemeProvider>,
document.getElementById("root")
);
App.js
export default function App() {
return (
<div>
<Notifs />
<MainContent />
</div>
);
}
MainContent.js
export default function MainContent() {
return (
<div className="container" style={{ marginTop: 20 }}>
<Switch>
<Route exact path="/" component={Landing} />
<Route path="/login" component={Login} />
<Route component={NoMatch} />
</Switch>
</div>
);
}

React-Router how to prevent from redirect

So I have a problem in my code that when I refresh my page it sends me a NotFound page, but when I don't refresh the page still works while changing the route (single page application)
My routes code:
import React, { Component } from 'react';
import { BrowserRouter, Route, Switch } from 'react-router-dom';
import Navigation from './src/containers/Navigation/Navigation';
import PageProjects from './src/containers/PageProjects/PageProjects';
import NewPost from './src/components/pages/Projects/NewPost/NewPost'
import Home from './src/components/pages/Home/Home';
import Calendar from './src/components/pages/Calendar/Calendar';
import Team from './src/components/pages/Team/Team';
import Settings from './src/components/pages/Settings/Settings';
import NotFound from './src/components/pages/NotFound/NotFound';
class App extends Component {
render() {
return (
<div className="App" style={{ height: '100%' }}>
<BrowserRouter>
<div>
<Navigation />
<Switch>
<Route path="/profile" component={Home} exact />
<Route path="/projects" component={PageProjects} exact />
<Route path="/calendar" component={Calendar} exact />
<Route path="/team" component={Team} exact />
<Route path="/newpost" component={NewPost} exact />
<Route path="/settings" component={Settings} exact />
<Route path="*" component={NotFound} exact />
</Switch>
</div>
</BrowserRouter>
</div>
);
}
}
export default App;
Only my "/profile" page works because it's the page that I'm sending the index.html file from my route server
app.get('/profile', function(req, res){
res.sendFile(__basedir + "/react/index.html");
});
My Navigation Component:
import React, { Component } from 'react';
import {BrowserRouter, Route, Switch} from 'react-router-dom';
import Toolbar from '../../components/Toolbar/Toolbar';
import SideBar from '../../components/SideBar/SideBar';
import Backdrop from '../../components/Backdrop/Backdrop';
import Home from '../../components/pages/Home/Home';
import Calendar from '../../components/pages/Calendar/Calendar';
import Team from '../../components/pages/Team/Team';
import Settings from '../../components/pages/Settings/Settings';
import NotFound from '../../components/pages/NotFound/NotFound';
class Navigation extends Component {
state = {
sideBarOpen: false,
}
barToggleClickHandler = () => {
this.setState((prevState) => {
return{sideBarOpen: !prevState.sideBarOpen};
});
};
backdropClickHandler = () => {
this.setState({sideBarOpen: false});
};
render() {
let backdrop;
if(this.state.sideBarOpen){
backdrop = <Backdrop click={this.backdropClickHandler}/>;
}
return (
<section className="Navigation">
<Toolbar barClickHandler={this.barToggleClickHandler} />
<SideBar show={this.state.sideBarOpen}/>
{backdrop}
<main style={{marginTop: '150px'}}>
</main>
</section>
);
}
}
export default Navigation;
What can I do?
Thanks
(I'm using Reactjs, Nodejs, Express..)
app.get('/', function(req, res){
res.sendFile(__basedir + "/react/index.html");
});
or use hashrouter
<HashRouter>
<div>
<Navigation />
<Switch>
<Route path="/profile" component={Home} exact />
<Route path="/projects" component={PageProjects} exact />
<Route path="/calendar" component={Calendar} exact />
<Route path="/team" component={Team} exact />
<Route path="/newpost" component={NewPost} exact />
<Route path="/settings" component={Settings} exact />
<Route path="*" component={NotFound} exact />
</Switch>
</div>
</HashRouter>
This should do the trick !
app.get('/', function(req, res){
res.sendFile(__basedir + "/react/index.html");
});

Resources