Jest, Enzyme test failing on window variable undefined - reactjs

I'm trying to create a window object to pass to my tests that includes a window variable. The test looks like:
const baseProps = {};
Enzyme.configure({ adapter: new Adapter() });
const baseWrapper = shallow(
<DivisionOrderIndex {...baseProps} />,
);
describe('<DivisionOrderIndex />', () => {
test('renders the MsDataGridServer', () => {
expect(baseWrapper.find(MsDataGridBulkDelete))
.toHaveLength(1);
});
The error message I receive is:
TypeError: Cannot read property 'read_only' of undefined
300 | </TitleButton>
301 | </div>
> 302 | {!window.jsdata.read_only && (
| ^
303 | <div id="page-buttons">
304 | Create New
305 | </div>
I've tried adding that data to my window object like so:
describe('<DivisionOrderIndex />', () => {
Object.defineProperty(window, 'jsdata', {
read_only: false,
});
test('renders the MsDataGridServer', () => {
expect(baseWrapper.find(MsDataGridBulkDelete))
.toHaveLength(1);
});
But I continue to get the same error. Is this the correct way to pass both the window object or the variables? Any input would be appreciated.

That's not how you add a property to an object. What you're doing here is adding a descriptor property that describes your object, there are only a few of them that you can set here e.g. writable, configurable etc.
You need to mock window object in the setupTests.js file like this:
window.jsdata = {
read_only: false
}

Related

How to override a component by creating a jest mock? (Error solving)

I have a test where I use a component called TableVirtuoso.
This component is imported as following:
import { TableVirtuoso } from 'react-virtuoso';
In my test I want to override the TableVirtuoso implementation by creating a mocked version with a HOC that set the initialItemCount attribute, so this is what I tried:
jest.mock('react-virtuoso', () => {
const { TableVirtuoso } = jest.requireActual('react-virtuoso');
const mockVirtuoso = (WrappedVirtuoso: ElementType) =>
class extends mockComponent<{ totalCount: number }, unknown> {
render() {
return (
<WrappedVirtuoso
initialItemCount={this.props?.totalCount}
{...this.props}
/>
);
}
};
return { TableVirtuoso: mockVirtuoso(TableVirtuoso) };
});
for reference, you can find this implementation here: https://github.com/petyosi/react-virtuoso/issues/26#issuecomment-1130584552
But when I tried this, I got the following error which I don't know how to fix:
TypeError: Class constructor cannot be invoked without 'new'
79 | wrapperOptions?: WrapperOptions,
80 | ) =>
> 81 | render(ui, {
| ^
82 | wrapper: props => <AllTheProviders {...props} {...wrapperOptions} />,
83 | ...options,
84 | });

React - Testing function passed in as props is called via button press

I am attempting to unit test a simple component that calls a function passed into it.
It is a simple footer component with two button, cancel/save.
When save is pressed, it should call the "handleSubmit" property I have passed into it but when attempting to test with jest, I cannot get the tests to pass.
Component:
function GSFooter({
handleSubmit,
}) {
return (
<Footer>
<FooterActionsWrap>
<CancelButton className="update-btn">
{" "}
<Link to={"/invoices"}>Cancel</Link>
</CancelButton>
<button
onSubmit={e => handleSubmit(e)}
className="wp-btn update-btn"
data-testid="submit-button"
>
Save Changes
</button>
</FooterActionsWrap>
</Footer>
);
}
and the test file
let handleSubmitMock = jest.fn();
test("it should run", () => {
const {container} = render(<GSFooter
handleSubmit={handleSubmitMock}
errors={{}}
/>);
fireEvent.click(getByTestId(container, 'submit-button'));
expect(handleSubmitMock).toBeCalled();
});
output:
expect(jest.fn()).toBeCalled()
Expected number of calls: >= 1
Received number of calls: 0
36 | const submitButton = getByTestId(container, 'submit-button');
37 | fireEvent.click(submitButton);
> 38 | expect(handleSubmitMock).toBeCalled();
| ^
39 | })
40 | });
41 |
UPDATED
After discussion with #cw23, he figured out that he's using onSubmit which is only triggered with fireEvent.submit instead of fireEvent.click! This is very useful info for developers facing a similar problem.
OLD ANSWER
You should call getByTestId directly. container is usually referred to DOM elements
test("it should run", () => {
const { getByTestId } = render(<GSFooter
handleSubmit={handleSubmitMock}
errors={{}}
/>);
fireEvent.click(getByTestId('submit-button'));
expect(handleSubmitMock).toBeCalled();
});

React testning library Undefined property in component

I dont understand why the property of the component is breaking the test?
Do i have to pass the component the properties? Even if i do pass the props i still get the same error.
I dont want to test implementation details.
Thanks in advance!
Here is my code:
it("render correctly", () => {
const { getByTestId } = render(<CalendarAvailability />);
const date = getByTestId("date");
expect(date).toBeInTheDocument();
})
The Component:
import {Availability, AvailableDate, Status, AvailableDateContainer} from './CalendarAvailability.css'
function CalendarAvailability({unitData}) {
return(
<Availability data-testid="calendar-availability-component">
<AvailableDateContainer>
<AvailableDate data-testid="date">{unitData.date}</AvailableDate>
</AvailableDateContainer>
</Availability>
)
}
export default CalendarAvailability;
The failing test:
● <CalendarAvailability /> › renders without breaking
TypeError: Cannot read property 'date' of undefined
5 | <Availability data-testid="calendar-availability-component">
6 | <AvailableDateContainer>
> 7 | <AvailableDate data-testid="date">{unitData.date}</AvailableDate>
| ^
8 | </AvailableDateContainer>
9 | </Availability>
I dont want to test the props only the rendring of the div?

React Testing Library Mock function not called

I am pretty new to testing and attempting to write what should be a simple test for our project...
test('Attempt Login', async () => {
const submitHandler = jest.fn( ()=> console.log('hello'))
const { debug, getByText, getByTestId, getByPlaceholderText } = render
(
<Router>
<LoginPage submitHandler={submitHandler} />
</Router>
)
fireEvent.change(getByPlaceholderText("Enter Username"), {
target: { value: "admin" }
});
fireEvent.change(getByPlaceholderText("Enter Password"), {
target: { value: "Password" }
});
fireEvent.click(getByTestId("login-btn"));
expect(submitHandler).toHaveBeenCalled()
})
My button inside of login
<Button data-testid="login-btn" type="submit" variant="contained" color="primary"
onClick={(event)=>submitHandler(event)}>
the testing error
expect(jest.fn()).toHaveBeenCalled()
Expected number of calls: >= 1
Received number of calls: 0
45 | fireEvent.click(getByTestId("login-btn"));
46 |
> 47 | expect(submitHandler).toHaveBeenCalled()
| ^
48 | })
49 |
50 |
Thanks in advance for any help. I spent way too long on this already -_-
EDIT: attempting to test for the results of clicking the login button
Heres what I'm going trying:
mock an Axios call to the login route
await waitForElement getByText('home')
expect getbytext('home')
Am I on the right track?
Do I need to import the redirect page component and place it inside the router? for example the component for it to redirect to it?
You should use waitFor in this case:
...
await waitFor(() => expect(submitHandler).toHaveBeenCalled())
...
More info HERE
As you already figured out, the problem is you are passing the submitHandler mock into LoginPage but you are not using that prop.
To answer your second question
How do I mock a function not passed in as a prop?
Here is how you can mock functions imported from different files with Jest:
import { submitForm } from './ajax.js'; // the function to mock
jest.mock('./ajax.js'); // jest mocks everything in that file
it('should call submitForm correctly', async () => {
submitForm.mockResolvedValue({ loggedIn: true });
render(<LoginPage />);
userEvent.click(screen.getByRole('button', { name: 'Login' }));
expect(submitForm).toHaveBeenCalledTimes(1);
expect(await screen.findByText('You have logged in successfully')).toBeInTheDocument();
});
Useful links
Mocking modules
Understanding Jest mocks
mockResolvedValue

Not getting expected result from .toHaveBeenCalledTimes() in react-testing-library

Anyhow, trying to test if a function has been called after its fired. The fireEvent is working as I get a console.log from that function. But the .toHaveBeenCalledTimes(1) returns 0. What have i missed?
If I have the handleLoginSubmit function in the parent and pass it as a prop down to the child and in the test everything passes. But if it's in the same component it fails. Using typescript if that has any meaning.
This is tested
import React, { FC } from 'react';
type Event = React.FormEvent<HTMLFormElement>;
interface Login {
handleLoginSubmit?: (event: Event) => React.ReactNode;
}
const Login: FC<Login> = () => {
const handleLoginSubmit = (_event: Event) => {
console.log('Firing' ); // This is logged
};
return (
<form data-testid='form' onSubmit={(event) => handleLoginSubmit(event)}>
<input data-testid='email'/>
<input data-testid='password'/>
<button data-testid='login-button'>login</button>
</form>
);
};
export default Login;
My test for submiting
it('should handle ClickEvents', () => {
const handleLoginSubmit = jest.fn();
const { getByTestId } = render(<Login/>);
expect(getByTestId('login-button')).toBeTruthy();
fireEvent.submit(getByTestId('form'));
expect(handleLoginSubmit).toHaveBeenCalledTimes(1);
});
Error message
● Login page › should handle ClickEvents
expect(jest.fn()).toHaveBeenCalledTimes(expected)
Expected number of calls: 1
Received number of calls: 0
32 | fireEvent.submit(getByTestId('form'));
33 |
> 34 | expect(handleLoginSubmit).toHaveBeenCalledTimes(1);
| ^
35 |
36 | });
37 | });
at Object.it (__tests__/components/Login.test.tsx:34:31)
You can't assert if the handleLoginSubmit function is to be called directly. Since it's defined in the private scope of Login SFC. You can't mock or spy on this function because you can't access it. So, you need to test it indirectly. Since you are using console.log in this function, we can spy console.log. If it's been called, that means the handleLoginSubmit function has been called.
E.g.
index.tsx:
import React, { FC } from "react";
type Event = React.FormEvent<HTMLFormElement>;
interface Login {
handleLoginSubmit?: (event: Event) => React.ReactNode;
}
const Login: FC<Login> = () => {
const handleLoginSubmit = (_event: Event) => {
console.log("Firing");
};
return (
<form data-testid="form" onSubmit={event => handleLoginSubmit(event)}>
<input data-testid="email" />
<input data-testid="password" />
<button data-testid="login-button">login</button>
</form>
);
};
export default Login;
index.spec.tsx:
import { render, fireEvent } from "#testing-library/react";
import Login from "./";
import React from "react";
it("should handle ClickEvents", () => {
const logSpy = jest.spyOn(console, "log");
const { getByTestId } = render(<Login />);
expect(getByTestId("login-button")).toBeTruthy();
fireEvent.submit(getByTestId("form"));
expect(logSpy).toHaveBeenCalledTimes(1);
});
Unit test result with 100% coverage:
PASS src/stackoverflow/59162138/index.spec.tsx
✓ should handle ClickEvents (42ms)
console.log node_modules/jest-mock/build/index.js:860
Firing
-----------|----------|----------|----------|----------|-------------------|
File | % Stmts | % Branch | % Funcs | % Lines | Uncovered Line #s |
-----------|----------|----------|----------|----------|-------------------|
All files | 100 | 100 | 100 | 100 | |
index.tsx | 100 | 100 | 100 | 100 | |
-----------|----------|----------|----------|----------|-------------------|
Test Suites: 1 passed, 1 total
Tests: 1 passed, 1 total
Snapshots: 0 total
Time: 3.987s, estimated 9s
Source code: https://github.com/mrdulin/jest-codelab/tree/master/src/stackoverflow/59162138

Resources