0

Sorry if this seems really obvious, but I've been trying to figure out for two days now. I'm writing a simple unit test in vitest that renders a component to screen and then calling screen.debug(). The test is

describe('Login unit tests', () => {
    it('renders to screen', () => {
        const loginProps = {
            handleSubmit: (e: MouseEvent<HTMLButtonElement>) => Promise.resolve(console.log('handleSubmit')),
            usernameProps: {
                value: "",
                placeholder: "Username",
                onChange: (e: ChangeEvent<HTMLInputElement>) => console.log('changeEvent'),
                type: "text"
            },
            passwordProps: {
                value: "",
                placeholder: "Password",
                onChange: (e: ChangeEvent<HTMLInputElement>) => console.log('changeEvent'),
                type: "password" 
            },
            isHidden: true,
        }
        render(<Login {...loginProps} />);
        screen.debug();
    })
})

The function definitions are just stand-ins with correct type, but would most likely be a vi.fn(). The component definition including interface of prop object is

interface LoginProps {
    handleSubmit: (e: MouseEvent<HTMLButtonElement>) => Promise<void>,
    usernameProps: InputProps,
    passwordProps: InputProps,
    isHidden: boolean,
}

const Login = ({
    handleSubmit,
    usernameProps,
    passwordProps,
    isHidden
}: LoginProps) => {
    return (
        <div className="login-container">
            <div className="login-bounding-box">
                <div className="login-title">Login</div>
                <form className="login-form">
            <input {...usernameProps} />
            <input {...passwordProps} />
            <button type="submit" onClick={handleSubmit}>Login</button>
        </form>
                <div className="error-msg" hidden={isHidden}>Username/password incorrect.</div>
                <div className="signup-msg">
                    Don't have an account? Sign up <Link to="/signup">here</Link>.
                </div>
            </div>
        </div>
    )
}

After running npm run test I get the following error:

TypeError: Cannot destructure property 'basename' of 'React__namespace.useContext(...)' as it is null.
 ❯ LinkWithRef node_modules/react-router-dom/index.tsx:427:11
 ❯ renderWithHooks node_modules/react-dom/cjs/react-dom.development.js:16305:18
 ❯ updateForwardRef node_modules/react-dom/cjs/react-dom.development.js:19226:20
 ❯ beginWork node_modules/react-dom/cjs/react-dom.development.js:21636:16
 ❯ beginWork$1 node_modules/react-dom/cjs/react-dom.development.js:27426:14
 ❯ performUnitOfWork node_modules/react-dom/cjs/react-dom.development.js:26560:12
 ❯ workLoopSync node_modules/react-dom/cjs/react-dom.development.js:26466:5
 ❯ renderRootSync node_modules/react-dom/cjs/react-dom.development.js:26434:7
 ❯ recoverFromConcurrentError node_modules/react-dom/cjs/react-dom.development.js:25850:20
 ❯ performConcurrentWorkOnRoot node_modules/react-dom/cjs/react-dom.development.js:25750:22

The interface for InputProps is

export default interface InputProps {
    value: string;
    placeholder: string | undefined;
    onChange: (e: ChangeEvent<HTMLInputElement>) => void;
    type: string;
}

I know the error is coming from the render(<Login />) line, but the error is cryptic and I'm not sure how to proceed. I assume there's something wrong with how I'm passing in the props object to <Login /> but I can't seem to figure out.

3
  • According to the error trace it seems the error is related to de Link component. Where is Link from? Are you using some library, like React Router? Commented Mar 14, 2023 at 16:33
  • I am using React Router, there is a <Link> in the <Login> component near the bottom Commented Mar 14, 2023 at 16:35
  • The error was the result of forgetting to wrap component in MemoryRouter for tests, thank you for help Commented Mar 14, 2023 at 16:38

2 Answers 2

3

Thalles Cezar's answer is correct, but just wanted to add for brevity that one usually uses MemoryRouter in tests and that render comes with a wrapper option. So the problem can be fixed by changing to:

render(<Login {...loginProps} />, { wrapper: MemoryRouter })
Sign up to request clarification or add additional context in comments.

Comments

2

You're probably wrapping a Router (like a BrowerRouter or MemoryRouter) somewhere in your app. But for the tests, the component is rendered by itself, without the rest of the app.

In that case, it's missing a Router, that React Router needs to find the basename property from its context.

Try rendering your test wrapped in a router, like so

import { BrowserRouter } from 'react-router-dom'
...
render(<BrowserRouter><Login {...loginProps} /></BrowserRouter>);

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.