i am having problem to test and mock an action which passed through connect HOC redux in a connected component. i recieve this error:
console.error node_modules/@testing-library/react/dist/act-compat.js:52 TypeError: props.getSearchByQuery is not a function at onFinishHandler (E:\React Js Projects\kyc-admin-panel\src\screen\Dashboard\Users\components\UserSearch.jsx:31:13) at onFinish (E:\React Js Projects\kyc-admin-panel\node_modules\rc-field-form\lib\Form.js:89:9) at E:\React Js Projects\kyc-admin-panel\node_modules\rc-field-form\lib\useForm.js:762:11
here is my UserSearch component:
import React, { useEffect, useState } from "react";
import { connect, useSelector } from "react-redux";
import { getUserSearchQuery } from "../../../../store/Actions/UserAction";
import { Col, Form, Row, Button, Input, Select } from "antd";
import { userSearchFields } from "../../../../constants/SearchFields";
const { Option } = Select;
const UserSearch = (props) => {
const roles = useSelector((state) => state.user.userRoles);
const [roleList, setRoleList] = useState(roles);
const [selectedRole, setSelectedRole] = useState("");
const [form] = Form.useForm();
useEffect(() => {
setRoleList(roles);
}, [roles]);
const onFinishHandler = (values) => {
const query = [];
for (let key in values) {
if (values[key] && values[key] !== undefined) {
query.push(key + "=" + values[key]);
}
}
if (query.length > 0) {
const joinQueries = query.join("&");
props.getUserSearchQuery(joinQueries);
props.getSearchByQuery(joinQueries);
} else {
props.getUserSearchQuery("");
props.getSearchByQuery("");
}
};
const onResetForm = () => {
form.resetFields();
props.getUserSearchQuery("");
props.getSearchByQuery("");
};
const fieldBox = [];
for (let i = 0; i < userSearchFields.length; i++) {
fieldBox.push(
<Col
lg={6}
md={12}
sm={24}
xs={24}
key={i}
style={{ textAlign: "right", float: "right" }}
>
{userSearchFields[i].name == "role" ? (
<Form.Item name={userSearchFields[i].name}>
<Select
onSelect={(value) => {
setSelectedRole(value);
}}
className="role-dropdown"
placeholder="نقش"
role="listbox"
>
{roleList.length > 0
? roleList.map((item, index) => {
return (
<Option
data-testid="option"
key={index}
value={item.value}
>
{item.label}
</Option>
);
})
: null}
</Select>
</Form.Item>
) : (
<Form.Item name={userSearchFields[i].name}>
<Input
placeholder={userSearchFields[i].placeholder}
name={userSearchFields[i].name}
autoComplete="off"
data-testid={userSearchFields[i].name}
/>
</Form.Item>
)}
</Col>
);
}
return (
<Form
form={form}
className="user-advanced-search-form"
onFinish={onFinishHandler}
>
<div className="user-searchbox-container">
<Row className="user-search-fields-containers" gutter={24}>
{fieldBox}
</Row>
<Row className="user-search-button-containers">
<Button
data-testid="search-button"
type="primary"
htmlType="submit"
className="search-button"
>
جستجو
</Button>
<Button
type="primary"
htmlType="button"
className="search-button filter-btn"
onClick={onResetForm}
data-testid="filter-button"
>
حذف فیلترها
</Button>
</Row>
</div>
</Form>
);
};
export default connect(null, {
getUserSearchQuery,
})(UserSearch);
my test file:
import React from "react";
import UserSearch from "../../../../screen/Dashboard/Users/components/UserSearch";
import { render, fireEvent, cleanup } from "../../../../test-utils";
import "@testing-library/jest-dom/extend-expect";
import { wait } from "@testing-library/react";
it("should call search function if one of the inputs filled and click on search button", async () => {
const searchHandler = jest.fn();
const { getByTestId, getByPlaceholderText } = render(
<UserSearch getUserSearchQuery={searchHandler} />
);
const searchButton = getByTestId("search-button");
const input = getByPlaceholderText("نام");
fireEvent.change(input, { target: { value: "masoud" } });
fireEvent.click(searchButton);
await wait(() => {
expect(searchHandler).toHaveBeenCalled();
});
});
test-utils.js
// test-utils.js
import React from "react";
import { render as rtlRender } from "@testing-library/react";
import { createStore, applyMiddleware } from "redux";
import { Provider } from "react-redux";
// Import your own reducer
import RootReducer from "./store/Reducers/index";
import thunk from "redux-thunk";
("use strict");
const customRender = (
ui,
{
initialState,
store = createStore(RootReducer, initialState, applyMiddleware(thunk)),
...renderOptions
} = {}
) => {
const Wrapper = ({ children }) => {
return <Provider store={store}>{children}</Provider>;
};
return rtlRender(ui, { wrapper: Wrapper, ...renderOptions });
};
// override render method
export { customRender as render };
// re-export everything
export * from "@testing-library/react";
package.json:
{
"name": "kyc-admin-panel",
"version": "0.1.0",
"private": true,
"dependencies": {
"@ant-design/icons": "^4.2.1",
"@testing-library/jest-dom": "^4.2.4",
"@testing-library/react": "^9.3.2",
"@testing-library/user-event": "^7.1.2",
"@types/jest": "^26.0.14",
"antd": "^4.3.1",
"axios": "^0.19.2",
"classnames": "^2.2.6",
"history": "^4.10.1",
"jest-environment-node": "^26.3.0",
"moment-jalaali": "^0.9.2",
"node-fetch": "^2.6.1",
"node-sass": "^4.14.1",
"node-snackbar": "^0.1.16",
"prop-types": "^15.7.2",
"qs": "^6.9.4",
"ramda": "^0.27.0",
"react": "^16.13.1",
"react-dom": "^16.13.1",
"react-helmet": "^6.1.0",
"react-redux": "^7.2.0",
"react-router-dom": "^5.2.0",
"react-scripts": "3.4.1",
"reactstrap": "^8.5.1",
"redux": "^4.0.5",
"redux-thunk": "^2.3.0",
"uuid": "^8.3.0"
},
"scripts": {
"start": "react-scripts start",
"build": "react-scripts build",
"test": "react-scripts test",
"eject": "react-scripts eject",
"coverage": "react-scripts test --env=jsdom --watchAll=false --coverage"
},
"eslintConfig": {
"extends": "react-app"
},
"browserslist": {
"production": [
">0.2%",
"not dead",
"not op_mini all"
],
"development": [
"last 1 chrome version",
"last 1 firefox version",
"last 1 safari version"
]
},
"devDependencies": {
"@babel/core": "^7.11.6",
"@babel/plugin-proposal-optional-chaining": "^7.11.0",
"@babel/plugin-syntax-dynamic-import": "^7.8.3",
"@babel/preset-env": "^7.11.0",
"@rollup/plugin-commonjs": "^11.0.2",
"babel-core": "^7.0.0-bridge.0",
"babel-jest": "^24.9.0",
"babel-plugin-module-resolver": "^4.0.0",
"babel-plugin-require-context-hook": "^1.0.0",
"babel-plugin-styled-components": "^1.11.1",
"babel-plugin-transform-es2015-modules-commonjs": "^6.26.2",
"babel-plugin-transform-es5-property-mutators": "^6.24.1",
"eslint": "^6.8.0",
"eslint-plugin-react-hooks": "^2.3.0",
"jest": "^24.9.0",
"jest-css-modules": "^2.1.0",
"prettier": "2.0.5",
"redux-mock-store": "^1.5.4"
}
}
i fixed this by exporting the component in two different kinds:
the connectedUserSearch is used for running in real browser and UserSearch is used just for test purposes. you can mock redux actions easily if you don't use connected component.