Test Nextjs <Link /> with Jest + Testing-Library - jestjs

When I'm testing the <Link /> behavior, expecting it to redirect to a certain route, there's a TypeError (Cannot read property 'push' of null).
This is the component I'm currently testing:
import React from "react"
import Link from "next/link"
const Sandbox = () => {
return (
<div>
<Link href="/about">
<a data-testid="mytest">Click Me</a>
</Link>
</div>
)
}
export default Sandbox
And this is the test I'm running:
import React from "react"
import { render, fireEvent } from "#testing-library/react"
import { useRouter } from "next/router"
import Sandbox from ".."
jest.mock("next/router", () => ({
useRouter: jest.fn(),
}))
describe("Sandbox", () => {
it.only("should navigate accordingly", () => {
const push = jest.fn()
useRouter.mockImplementationOnce(() => ({
asPath: "/",
push,
}))
const { getByTestId } = render(<Sandbox />)
const mytest = getByTestId("mytest")
fireEvent.click(mytest)
expect(push).toHaveBeenCalledWith("/about")
})
})
I believe that I've mocked everything I need, so I don't really understand why the router can't actually "push".
What am I missing here?

Related

Fetch request unable to get backend data due to Uncaught AxiosError - next.js and express.js

I'm trying to fetch some backend data on my Express.js backend which looks like this:
const express = require('express')
const app = express()
app.get("/api", (req, res) => {
res.json({"data": ["data1", "data2", "data3"]})
})
app.listen(5000, () => { console.log("Server started on port 5000, hi") })
Every time the specific page loads I want it to fetch the {"data": ["data1", "data2", "data3"]} from the backend, I added a button that makes the same request for testing as well. Whenever I click the button and whenever the page loads I get this error:
I don't really understand why I'm getting this error, here is my next.js code:
import React, { Component, useEffect, useState } from 'react';
import axios from 'axios';
export default function Product() {
const [backendData, setBackendData] = useState([{}])
useEffect(() => {
axios.get('/api').then(
response => response.json()
).then(
data => {
setBackendData(data)
}
)
console.log("ran")
}, [])
const test = () => {
axios.get('/api').then(
response => response.json()
).then(
data => {
setBackendData(data)
}
)
console.log("test clicked")
}
return (
<div style={styles.container}>
<div style={styles.speechTitle}>Talk to us, tell us about your day...</div>
<div style={styles.speechBox}>
Test
</div>
<button onClick={console.log("start")}>
Start
</button>
<button onClick={console.log("stop")}>Stop</button>
<button onClick={console.log("reset")}>Reset</button>
{(typeof backendData.data === 'undefined') ? (
<p>Loading...</p>
) : (
backendData.data.map((data, i) => (
<p key={i}>{data}</p>
))
)}
<button onClick={() => test()}>asdasd</button>
</div>
);
}
I'm running this component called Product you see above in this file called product.js which is in my pages folder:
import React from 'react';
import { ThemeProvider } from 'theme-ui';
import { StickyProvider } from 'contexts/app/app.provider';
import theme from 'theme';
import SEO from 'components/seo';
import Layout from 'components/layout';
import Product from 'components/product-input'
export default function ProductPage() {
return (
<ThemeProvider theme={theme}>
<StickyProvider>
<Layout>
<SEO title="Talkhappi" />
<Product/>
</Layout>
</StickyProvider>
</ThemeProvider>
);
}
I am also getting a network error when I open up the network tab in developer tools:
I'm unsure how to fix this problem and retrieve the data I want to retrieve from my backend running at port 5000.
You seem to have to call your apis at port 5000 instead of 3000 you did.
const baseURL = 'http://localhost:5000';
const test = () => {
axios.get(baseURL + '/api').then(
response => response.json()
).then(
data => {
setBackendData(data)
}
)
console.log("test clicked")
}

NEXT.js Uncaught (in promise) TypeError: NetworkError when attempting to fetch resource

Hi guys I've created fullstack app with NEXT.js, TypeScript, NODE.js and MongoDB. Generally this is decode\encode app with all functionality on Backend side. My backend side is already deployed on heroku. Front-end I still have on a localhost till I solve this problem. When I fetch all of my data from Backend, first I see error mesage, then data from Backend.
Here below error msg:
And here below my code:
import { ListProps } from "./List.props";
import styles from "./List.module.css";
import { P } from '../'
import React, { useEffect, useState } from "react";
import axios from "axios";
export const List = ({ children, className, ...props }: ListProps): JSX.Element => {
const [blogs, setBlogs] = useState<any[]>([]);
useEffect(() => {
const fetchData = async () => {
await fetch(`https://node-test-mongo.herokuapp.com/api/blog`)
.then((response) => {
return response.json();
})
.then((data) => {
setBlogs(data.blogs)
})
};
fetchData();
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [blogs]);
return (
<div
className={styles.ul}
{...props}
>
{blogs && blogs.map((blog, index) => (
<ul key={blog._id}>
<li className={styles.li}>
<P className={styles.title} size='l'>{blog.title}</P>
<P size='l'>description={blog.text} </P>
</li>
</ul>
))}
</div>
)
};
I suppose, that there could be something wrong with my data fetching, but I'm not sure.
Thank you in advance!:)

Trouble with dividing an API into multiple components instead of just one

I'm having trouble for the past few days in this problem.
Explanation of the problem:
I'm using Axios in order to get data inside of state (Pokémon's), But, everything is rendering inside one component (creating an array and list and shows the list) , while I need every Pokémon I get from the API to be inside his own component (so that I can images per Pokémon and etc.)
Does anybody perhaps knows how to do so? If u do, please answer and explain to me (And if it wont be any trouble, modify the code), Thanks in advance, Mikey. (Using react-ts, node.js and axios)
import React, { useState, useEffect } from 'react';
import axios from 'axios';
export default function PokeCont() {
const [pokemons, setPokemons] = useState<any>();
const onClick = () => {
axios.get('https://pokeapi.co/api/v2/pokemon?limit=6').then((response) => {
setPokemons(response.data.results);
});
};
useEffect(() => {
onClick();
}, []);
return (
<div>
{pokemons &&
pokemons.map((pokemon: any) => (
<div key={pokemon.name}>
<p>{pokemon.name}</p>
</div>
))}
</div>
);
}
Here is an example ;)
import { useState, useEffect } from "react";
import axios from "axios";
export default function App() {
return (
<div className="App">
<PokemonContainer />
</div>
);
}
function PokemonContainer() {
const [pokemons, setPokemons] = useState<any[]>([]);
const onClick = () => {
axios.get("https://pokeapi.co/api/v2/pokemon?limit=6").then((response) => {
setPokemons(response.data.results);
});
};
useEffect(() => {
onClick();
}, []);
return (
<div>
{pokemons &&
pokemons.map((pokemon) => (
<PokemonItem key={pokemon.name} info={pokemon} />
))}
</div>
);
}
function PokemonItem({ info }) {
return (
<div>
<h2>{info.name}</h2>
<img src={info.image} width="100" height="100" alt=""></img>
</div>
);
}
Add necessary changes to components like export default and types.
import React, { useState, useEffect } from 'react'
import axios from 'axios'
***Component 1: Pokemon***
Pokemon = props => {
return (
<>
<div key={props.name}>
<p>{props.name}</p>
</div>
</>
)
}
***Component 2: Parent Pokemon which renders your Pokemon as Component ***
export default function PokeClass() {
const [pokemons, setPokemons] = useState()
const onClick = () => {
axios.get("https://pokeapi.co/api/v2/pokemon?limit=6").then((response) => {
setPokemons(response.data.results)
})
}
useEffect(() => {
onClick()
}, []);
return (
<>
{pokemons.map((item => {
return <Pokemon name={item.name} />
}))}
</>
)
}

Why is react admin login page only shown for one second?

I have built a website with react admin. Now i want to add the basic login page from react admin.
For this I have added the simple authProvider which passes all username and password combinations. But now the login page is only shown for one second when I click on the logout button and then the website always jumps back to the dashboard.
I have tried a lot but can't find the error.
Maybe someone has an idea what it could be or had the same problem before?
Here is my code snippet from App.js:
function App() {
return(
<Admin
dashboard={Dashboard}
authProdiver={authProvider}
dataProvider={dataProvider}
customRoutes={customRoutes}
theme={theme}
layout={MyLayout}
>
<Resource
...
/>
...
</Admin>
);
}
export default App;
I added the basic authProvider from the tutorial:
const authProvider = {
// authentication
login: ({ username }) => {
localStorage.setItem('username', username);
// accept all username/password combinations
return Promise.resolve();
},
logout: () => {
localStorage.removeItem('username');
return Promise.resolve();
},
checkError: () => Promise.resolve(),
checkAuth: () =>
localStorage.getItem('username') ? Promise.resolve() : Promise.reject(),
getPermissions: () => Promise.reject('Unknown method'),
};
export default authProvider;
my own layout is:
MyLayout.js:
import React from 'react';
import TreeMenu from '#bb-tech/ra-treemenu';
import { Layout } from 'react-admin';
import MyAppBar from './MyAppBar';
import { ProfileProvider } from './MyProfile/Profile.js';
const MyLayout = (props) => (
<ProfileProvider>
<Layout {...props} appBar={MyAppBar} menu={TreeMenu} />
</ProfileProvider>
);
export default MyLayout;
MyAppBar.js:
import React from "react";
import { AppBar } from "react-admin";
import MyUserMenu from "./MyUserMenu";
const MyAppBar = props =>
<AppBar {...props}
userMenu={<MyUserMenu />}
/>;
export default MyAppBar;
MyUserMenu.js:
import React from 'react';
import { UserMenu, MenuItemLink} from 'react-admin';
import SettingsIcon from '#material-ui/icons/Settings';
const MyUserMenu = (props) => {
return (
<UserMenu {...props}>
<MenuItemLink
to="/my-profile"
primaryText="Mein Profil"
leftIcon={<SettingsIcon />}
/>
</UserMenu>
);
};
export default MyUserMenu;

Only one element of type cardNumber can be created

I am trying to display my stripe component, but I am getting this error:
IntegrationError: Only one element of type cardNumber can be created.
I don't know why, since I'm only using it once in my entire app
Any ideas?
This is my index
import ReactDOM from 'react-dom';
import App from './App';
import * as serviceWorker from './serviceWorker';
import { loadStripe } from "#stripe/stripe-js";
import { Elements } from "#stripe/react-stripe-js";
import MyComponent from './components/StripeComponent';
const promise = loadStripe("xxx-xxx-xxx");
ReactDOM.render(
<React.StrictMode>
<Elements stripe={promise}>
<MyComponent/>
</Elements>
</React.StrictMode>,
document.getElementById('root')
);
And this is my component
import React from "react";
import {
useElements,
} from "#stripe/react-stripe-js";
const MyComponent: React.FC= (props)=>{
const elements = useElements();
const cardNumberElement = elements?.create('cardNumber', {
placeholder: ''
});
const cardExpiryElement = elements?.create('cardExpiry', {
placeholder: ''
});
const cardCvvElement = elements?.create('cardCvc', {
placeholder: ''
});
cardNumberElement?.mount('#card-number-element')
cardExpiryElement?.mount('#card-expiry-element')
cardCvvElement?.mount('#card-cvv-element')
const handleSubmit = async (ev: any) => {
ev.preventDefault();
};
return (
<form id="payment-form" onSubmit={handleSubmit}>
<div id="card-number-element"></div>
<div id="card-expiry-element"></div>
<div id="card-cvv-element"></div>
</form>
);
}
export default MyComponent
Seems it is because you create and mount the card components in the functional component body they are executed on every render of the component, i.e. as an inadvertent side-effect.
Place the creation and mounting logic in an useEffect hook with an empty dependency array so that it is invoked only once when the component mounts.
import React, { useEffect } from "react";
import { useElements } from "#stripe/react-stripe-js";
const MyComponent: React.FC = (props) => {
const elements = useElements();
// Effect hook to run once on component mount
useEffect(() => {
const cardNumberElement = elements?.create("cardNumber", {
placeholder: ""
});
const cardExpiryElement = elements?.create("cardExpiry", {
placeholder: ""
});
const cardCvvElement = elements?.create("cardCvc", {
placeholder: ""
});
cardNumberElement?.mount("#card-number-element");
cardExpiryElement?.mount("#card-expiry-element");
cardCvvElement?.mount("#card-cvv-element");
}, []); // <-- empty dependency array
const handleSubmit = async (ev: any) => {
ev.preventDefault();
};
return (
<form id="payment-form" onSubmit={handleSubmit}>
<div id="card-number-element"></div>
<div id="card-expiry-element"></div>
<div id="card-cvv-element"></div>
</form>
);
};
useEffect(() => {
if (elements) {
const cardNumberElement =
elements.getElement("cardNumber") || // check if we already created element
elements.create("cardNumber", defaultInputStyles); // create if dont
cardNumberElement.mount("#numberInput");
}
}, [elements]);
I had the same problem, in my case, I had a reference to CardNumberElement in another section of my code. After removing it, everything worked fine.

Resources