e.currentTarget.input.value = undefined?? ReactJS Testing Library - reactjs

I'm trying to make a test using ReacTJS Testing Library for a to-do list form, the test I want to implement it's supposed to check if the form has called a function from parameter but I'm having a hard time receiving an error I cannot solve (error at the end of the question).
What am I doing wrong here??
My form component code:
import { FC } from "react";
import "./TaskCreator.css";
interface TaskCreatorProps {
sendNewTask: (text: string) => void;
}
const TaskCreator: FC<TaskCreatorProps> = ({ sendNewTask }) => {
return (
<form
action="#"
className="todo__form"
onSubmit={(e) => {
e.preventDefault();
sendNewTask(e.currentTarget.taskInput.value);
}}
>
<input
name="taskInput"
className="form__input"
type="text"
placeholder="New to do..."
required
/>
<button className="form__submit" type="submit">
Add new task
</button>
</form>
);
};
export default TaskCreator;
The test's code:
import { render, screen } from "#testing-library/react";
import userEvent from "#testing-library/user-event";
import TaskCreator from "./TaskCreator";
describe("Given a TaskCreator component", () => {
test("When the user creates a new task, it should be sent by parameter to App component", () => {
const callbackFn = jest.fn();
render(<TaskCreator sendNewTask={callbackFn} />);
const formInput = screen.getByRole("textbox");
userEvent.type(formInput, "test");
const formButton = screen.getByRole("button");
userEvent.click(formButton);
expect(formInput).toHaveValue("test");
expect(callbackFn).toHaveBeenCalled();
});
});
The error:
● Given a TaskCreator component › When the user creates a new task, it should be sent by parameter to App component
TypeError: Cannot read properties of undefined (reading 'value')
13 | onSubmit={(e) => {
14 | e.preventDefault();
> 15 | sendNewTask(e.currentTarget.taskInput.value);
| ^
16 | }}
17 | >
18 | <input
● Given a TaskCreator component › When the user creates a new task, it should be sent by parameter to App component
expect(jest.fn()).toHaveBeenCalled()
Expected number of calls: >= 1
Received number of calls: 0
15 |
16 | expect(formInput).toHaveValue("test");
> 17 | expect(callbackFn).toHaveBeenCalled();
| ^
18 | });
19 | });
20 |
at Object.<anonymous> (src/components/TaskCreator/TaskCreator.test.tsx:17:24)
Test Suites: 2 failed, 2 total
Tests: 1 failed, 1 total
Snapshots: 0 total
Time: 0.764 s, estimated 1 s
I have already tried to test with async and await but I didn't get any result.

Related

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();
});

write unit test for event handlers that updates some state in react using jest and enzyme

I am trying to write some unit tests for the event handlers that I wrote inside my component. I would like to write tests for the states updates inside that event handlers.
For example I have the following function that are called onMouseDown inside the component. How can I write some tests about that.
const [visible, setVisibility ] = useState(false);
const onSelection = () => {
setVisibility(!visible)
};
<div onMouseDown ={()=> onSelection(items)}>Click</div>
{visible && <div>simple text</div>}
Can anybody guide me through there. Thanks in advance
Suppose the component like this:
index.tsx:
import React, { useState } from 'react';
export default function Example() {
const [visible, setVisibility] = useState(false);
const onSelection = () => {
setVisibility(!visible);
};
return (
<div>
<div onMouseDown={() => onSelection()}>Click</div>
{visible && <div>simple text</div>}
</div>
);
}
We test the behavior of the component from the user's perspective.
We should test: What is the component rendering before triggering the mousedown event and what is rendered after the visible state changes.
index.test.tsx
import { shallow } from 'enzyme';
import React from 'react';
import Example from './';
describe('70577146', () => {
test('should pass', () => {
const wrapper = shallow(<Example />);
const button = wrapper.find('div[children="Click"]');
expect(wrapper.find('div[children="simple text"]').exists()).toBeFalsy();
button.simulate('mousedown');
expect(wrapper.find('div[children="simple text"]').exists()).toBeTruthy();
});
});
Test result:
PASS examples/70577146/index.test.tsx (11.966 s)
70577146
✓ should pass (11 ms)
-----------|---------|----------|---------|---------|-------------------
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: 13.599 s, estimated 14 s

How to use .contains(nodeOrNodes) API when the contained react element has an arrow function event handler?

For below example, .contains(nodeOrNodes) => Boolean API works fine.
index.tsx:
import React from 'react';
const Comp = ({ onChange }) => (
<form>
<input type="text" placeholder="username" onChange={onChange} />
</form>
);
export default Comp;
index.test.tsx:
import React from 'react';
import { shallow } from 'enzyme';
import Comp from '.';
describe('Comp', () => {
it('should render', () => {
const noop = () => null;
const wrapper = shallow(<Comp onChange={noop} />);
expect(
wrapper.contains(
<form>
<input type="text" placeholder="username" onChange={noop} />
</form>,
),
).toBeTruthy();
});
});
Unit test results:
PASS src/stackoverflow/46133847/02/index.test.tsx
Comp
✓ should render (13ms)
-----------|----------|----------|----------|----------|-------------------|
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: 7.754s, estimated 24s
But, if I change the onChange event handler using the arrow function:
index.ts:
import React from 'react';
const Comp = ({ onChange }) => (
<form>
<input type="text" placeholder="username" onChange={(e) => onChange(e)} />
</form>
);
export default Comp;
The unit test will fail.
FAIL src/stackoverflow/46133847/02/index.test.tsx
Comp
✕ should render (18ms)
● Comp › should render
expect(received).toBeTruthy()
Received: false
13 | </form>,
14 | ),
> 15 | ).toBeTruthy();
| ^
16 | });
17 | });
18 |
at Object.it (src/stackoverflow/46133847/02/index.test.tsx:15:7)
Test Suites: 1 failed, 1 total
Tests: 1 failed, 1 total
Snapshots: 0 total
Time: 7.689s, estimated 25s
I think the test failed because the arrow function created a new function reference. This new function has a different reference with noop function passed into Comp.
But what I want is, is there any way like expect.any(Function) of jestjs, just to assert whether or not the wrapper contains any function of onChange event handler?
Package versions:
"enzyme": "^3.10.0",
"jest": "^24.9.0",
To be honest, I do not think using .contains(nodeOrNodes) is good way for this situation.
My personal suggestion is followed by below:
let inputComponent = wrapper.find("input[placeholder]='username'");
expect(inputComponent.length).toBe(1); // This tests your component is exists rather than contains)
expect(inputComponent.props().onChange).toEqual(noop); //This tests onChange function is equal your mock function
Please vote it, if it works for you

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

React Native Jest Syntax Error: Unterminated Regular Expression

I'm getting this strange syntax error whilst running a simple test for a Todo demo I'm writing. I have added the code and test in the same file so that you can see what's going on.
Any help will be greatly appreciated.
import { Text } from 'react-native';
import React from 'react';
import { shallow } from 'enzyme';
const TodoItem = () => {
return(
<Text>{Hello}</Text>
);
};
export default TodoItem;
describe('<TodoItem', () => {
it('should update uncompleted task to complete when pressed', () => {
const wrapper = shallow(<TodoItem />)
});
});
Test Result
Run: jest
FAIL __tests__/components/TodoItem.spec.tsx
● Test suite failed to run
SyntaxError: /Users/jonesagyemang/Projects/side/Todoist/__tests__/components/TodoItem.spec.tsx: Unterminated regular expression (7:19)
5 | const TodoItem = () => {
6 | return(
> 7 | <Text>{Hello}</Text>
| ^
8 | );
9 | };
10 |
at Object.raise (../../../../../usr/local/lib/node_modules/jest/node_modules/#babel/parser/lib/index.js:6322:17)
Test Suites: 1 failed, 1 total
Tests: 0 total
Snapshots: 0 total
Time: 0.2s
Ran all test suites related to changed files.
Watch Usage: Press w to show more.
I'm assuming it's a text that you want to display with Text component. Try removing the {} for the Hello since it isn't a variable or a function in your code.
const TodoItem = () => {
return(
<Text>Hello</Text>
);
};

Resources