Testing react form - AssertionError - reactjs

I'm trying to test a click on a react component that renders a form, however it fails the third test with the following error
AssertionError: expected [Function: proxy] to have a property 'callCount' of 1, but got 0
Here's the test i wrote:
form.test.js
describe('<Form />', () => {
it('renders without exploding', () => {
shallow(<Form />);
});
it('renders an `.login_form`', () => {
const wrapper = shallow(<Form />);
expect(wrapper.find('.login_form')).to.have.length(1);
});
it('simulates click events', () => {
const onButtonClick = sinon.spy();
const wrapper = shallow(
<Form handleSubmit={ onButtonClick } />,
);
wrapper.find('.submitBtn').simulate('click');
expect(onButtonClick).to.have.property('callCount', 1);
});
});
and the component itself:
form.jsx
function Form(props) {
return (
<form onSubmit={ props.handleSubmit } className='form-horizontal login_form'>
<div className='form-group'>
<div className='col-md-offset-4 col-md-4 col-xs-12'>
<input className='form-control' type='text' id='username' value={ props.username } onChange={ props.handleUsername } placeholder='Username' required />
</div>
</div>
<div className='form-group'>
<div className='col-md-offset-4 col-md-4 col-xs-12'>
<input className='form-control' type='password' id='password' value={ props.password } onChange={ props.handlePassword } placeholder='Password' required />
</div>
</div>
<div className='form-group'>
<div className='col-md-offset-4 col-md-4 col-xs-12'>
<button className='btn btn-block btn-default submit submitBtn' type='submit'>Log in</button>
{
props.error !== '' &&
<p className='login_error no-margin'>
{props.error}
</p>
}
</div>
</div>
</form>
);
}
Form.propTypes = {
username: React.PropTypes.string,
password: React.PropTypes.string,
error: React.PropTypes.string,
handleUsername: React.PropTypes.func,
handlePassword: React.PropTypes.func,
handleSubmit: React.PropTypes.func,
};
export default Form;
What i am trying to do here is that i want to test that when the login button is clicked, the onSubmit handler is called. What am i doing on here?

Try to submit form this way:
wrapper.find("form").simulate("submit");

Related

React testing a form

I am trying to test my sign-in form for a react app. Basically the user signs up, and then the form will make an api post request to node.js server that will then post the data into a MySQL databases
The HTML for the form is:
<div className="signup">
<div className="signup__wrapper">
<div className="signup__banner">
<h2 className="signup__logo">Leep</h2>
<h2 className="signup__title">Please Sign Up to find artist</h2>
</div>
<form className="form" onSubmit={handleSubmit} autoComplete='off'>
<div className="form__container">
<input
className={!email.error? `form__input ${email.value? 'form__valid':''}`: "form__input error"}
type='text'
id='email'
data-testid='email'
name='email'
value={email.value}
onChange={handleEmail}
autoComplete='off'
/>
<label className="form__label" htmlFor="email">What's your email?</label>
</div>
<div className="form__container">
<input
className={!confirmEmail.error? `form__input ${confirmEmail.value? 'form__valid':''}`: "form__input error"}
type='text'
id='confirmemail'
data-testid='confirmemail'
name='confirmemail'
value={confirmEmail.value}
onChange={handleConfirmEmail}
/>
<label className="form__label" htmlFor="confirmemail">Please confirm your email?</label>
</div>
<div className="form__container">
<input
className={!username.error? `form__input ${username.value? 'form__valid':''}`: "form__input error"}
type='text'
id='username'
data-testid='username'
name='username'
value={username.value}
onChange={handleUserName}
/>
<label className="form__label" htmlFor="username">What's your user name?</label>
</div>
<div className="form__container">
<input
className={!password.error? `form__input ${password.value? 'form__valid':''}`: "form__input error"}
type='password'
id='password'
name='password'
data-testid='password'
value={password.value}
onChange={handlePassword}
/>
<label className="form__label" htmlFor="password">What's your password?</label>
</div>
<div className="form__container">
<input
className={!confirmPassword.error? `form__input ${confirmPassword.value? 'form__valid':''}`: "form__input error"}
type='password'
id='confirmPassword'
data-testid='confirmPassword'
name='confirmPassword'
value={confirmPassword.value}
onChange={handleConfirmPassword}
/>
<label className="form__label" htmlFor="confirmPassword">Please confirm your password</label>
</div>
<button className="form__btn" type="submit">
Sign up
</button>
<div className="signup__redirect">
<h3 className="signup__redirect--message">Already have an account?</h3>
<Router>
<Link className="signup__redirect--link" to='/login'>
Log in.
</Link>
</Router>
</div>
</form>
</div>
</div>
And here is the code that I am trying to use to test the form submission:
import React from "react";
import ReactDOM from "react-dom";
import SignupPage from "./SignupPage";
import {render, screen, waitFor } from '#testing-library/react'
import user from '#testing-library/user-event'
it("renders without crashing", ()=> {
const div = document.createElement("div")
ReactDOM.render(<SignupPage></SignupPage>, div)
})
describe('SignUpform', () => {
const onSubmit = jest.fn();
window.alert = jest.fn()
beforeEach(() => {
onSubmit.mockClear()
render(<SignupPage onSubmit={onSubmit} />)
});
it('onSubmit is called when all fields pass validation', async () => {
window.alert = () => {};
user.type(getEmail(), 'test#gmail.com')
user.type(confirmEmail(), 'test#gmail.com')
user.type(getUsername(), 'test')
user.type(getPassword(), '1234')
user.type(getConfirmPassword(), '1234')
user.click(screen.getByRole('button'))
await waitFor(() => {
expect(onSubmit).toBeCalledWith({
email: 'test#gmail.com',
confirmemail: 'test#gmail.com',
username: 'test',
password: '1234',
confirmpassword: '1234',
})
expect(onSubmit).toHaveBeenCalledTimes(1)
})
});
})
function getEmail () {
return screen.getByTestId('email', {
name: /email/i
})
}
function confirmEmail () {
return screen.getByTestId('confirmemail', {
name: /confirmemail/i
})
}
function getUsername () {
return screen.getByTestId('username', {
name: /username/i
})
}
function getPassword () {
return screen.getByTestId('password', {
name: /password/i
})
}
function getConfirmPassword() {
return screen.getByTestId('confirmPassword', {
name: /confirmpassword/i
})
}
console out put of the error that is followed by the html elements of the form
here is where the code is stating the error I am not sure what is wrong.

Pass data from a form component to another component in react hook

I have the following form, and I need that when submitting the form, its information is displayed in a new component.
Perhaps the issue of redirecting to the other component could be done by creating a route. But I don't know how said component obtains the information of the form
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.6.3/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.6.3/umd/react-dom.production.min.js"></script>
import {useState, useRef} from 'React'
export default const FormX = () => {
const [formValues, setFormValues] = useState({
name: "",
priceUnitary: "",
size: "",
description: "",
});
const inputFileRef = useRef();
const handleChange = (event) => {
const { name, value } = event.target;
console.log(name, value);
setFormValues({ ...formValues, [name]: value });
};
const handleSubmit = (e) => {
e.preventDefault();
console.log(formValues);
console.log(inputFileRef.current.files);
};
return (
<>
<form id="formu" onSubmit={handleSubmit} className="row">
<h1>FORM SEND</h1>
<div className="col-md-6">
<label>Name</label>
<input
placeholder="Text input"
name="name"
value={formValues.name}
onChange={handleChange}
/>
</div>
<div className="col-md-6">
<label>Size</label>
<input
type="number"
placeholder="Text input"
name="size"
value={formValues.size}
onChange={handleChange}
/>
</div>
<div className="col-md-6">
<label>Price Unitary</label>
<input
type="number"
placeholder="Text input"
name="priceUnitary"
value={formValues.priceUnitary}
onChange={handleChange}
/>
</div>
<div className="col-md-6">
<label>Description</label>
<input
placeholder="Text input"
name="description"
value={formValues.description}
onChange={handleChange}
/>
</div>
<div className="col-md-6">
<label>File / Image</label>
<input type="file" ref={inputFileRef} />
</div>
<button type="submit" className="color-primary">
Save
</button>
</form>
</>
);
};
Link:
https://codesandbox.io/s/send-form-dcj5v?file=/src/App.js
You can hide your form by change your state on form sumbit and display another component. You have to pass formValue as props in View component. Now think you have better idea what you have to do...
Here i added new component, that display form value on submit
App.js
import { useState, useRef } from "react";
import View from "./View";
const FormX = () => {
const [formValues, setFormValues] = useState({
name: "",
priceUnitary: "",
size: "",
description: ""
});
const [isFormVisible, setIsFormVisible] = useState(true);
const inputFileRef = useRef();
const handleChange = (event) => {
const { name, value } = event.target;
console.log(name, value);
setFormValues({ ...formValues, [name]: value });
};
const handleSubmit = (e) => {
e.preventDefault();
console.log(formValues);
console.log(inputFileRef?.current?.files);
setIsFormVisible(false);
};
return (
<>
{isFormVisible ? (
<form id="formu" onSubmit={handleSubmit} className="row">
<h1>FORM SEND</h1>
<div className="col-md-6">
<label>Name</label>
<input
placeholder="Text input"
name="name"
value={formValues?.name}
onChange={handleChange}
/>
</div>
<div className="col-md-6">
<label>Size</label>
<input
type="number"
placeholder="Text input"
name="size"
value={formValues.size}
onChange={handleChange}
/>
</div>
<div className="col-md-6">
<label>Price Unitary</label>
<input
type="number"
placeholder="Text input"
name="priceUnitary"
value={formValues.priceUnitary}
onChange={handleChange}
/>
</div>
<div className="col-md-6">
<label>Description</label>
<input
placeholder="Text input"
name="description"
value={formValues.description}
onChange={handleChange}
/>
</div>
<div className="col-md-6">
<label>File / Image</label>
<input type="file" ref={inputFileRef} />
</div>
<button type="submit" className="color-primary">
Save
</button>
</form>
) : (
<View data={formValues} />
)}
</>
);
};
export default FormX;
View.js
import React from "react";
const View = ({ data }) => {
return (
<div>
<p>Name: {data?.name}</p>
<p>priceUnitary: {data?.priceUnitary}</p>
<p>description: {data?.description}</p>
</div>
);
};
export default View;

how to use onClick on react-ripples?

i create component MyButton1 to use react-ripples, but when i use onClick in MyButton1, it's not working
it's working normaly if i use button tag but i want to use my component
my code :
export const Register = () => {
const postRegister = () => {
axios.post('http://localhost:3001/api/user/register', {
email: email,
username: username,
password: password,
})
.then(() => {
alert('đăng kí thành công')
})
.catch(err => alert(err));
}
const register = () => {
console.log('onClick work')
postRegister()
}
return (
<div className="login-site">
<form className='form-login'>
<h1 className='login-form-title'>Đăng Kí</h1>
<div className="form-row">
<input onChange={getEmail} type="email"
name="email" placeholder="Your Email" />
</div>
<div className="form-row">
<input value={username} onChange={getUsername} type="email"
name="email" autocomplete="off" placeholder="Tên đăng nhập" />
</div>
<div className="form-row">
<input value={password} onChange={getPassword} type="password"/>
</div>
<div className="form-button-right">
Quên mật khẩu?
</div>
<div className="form-button-center">
<div>
<MyButton1 onClick={register}>Đăng kí</MyButton1>
</div>
Đăng nhập
</div>
</form>
</div>
);
}
in MyButton1 component code:
export const MyButton1 = (props) => {
return (
<Ripples color="rgba(255,255,255,0.5)" during={650}>
<Button className='my-button-1'>{props.children}</Button>
</Ripples>
);
}
Your MyButton1 doesn't have an onClick props
Since the onClick handler is only available on the native HTML element, you would need to pass it down to your Button component which supposedly also have that handler already.
Something like this:
export const MyButton1 = (props) => {
return (
<Ripples color="rgba(255,255,255,0.5)" during={650}>
<Button className="my-button-1" onClick={props.onClick}>{props.children}</Button>
</Ripples>
);
};

React function Component Validation

I am a beginner in react. I was working on the react function component with forms using hooks. Can anyone please tell how can I apply validation on email text when it is invalid or empty, and disable the continue button if the form is not valid.
import React, { useState } from "react";
const ForgotPassowrd = () => {
const [emailId, setemailId] = useState("");
const forgotPasswordClick = (event) => {};
return (
<div>
<div className="NewPassword-form form_wrapper">
<div className="form-body">
<form action="#">
<div>
<div className="form-group">
<label htmlFor="password">Email-Id</label>
<div className="input-group">
<input type="text" className="form-control" value={emailId} onChange={(event)=>
setemailId(event.target.value)}/>
</div>
</div>
<button type="button" onClick={forgotPasswordClick} className="btn btn-lg
btn-block">Continue</button>
</div>
</form>
</div>
</div>
</div>
);
};
export default ForgotPassowrd;
**Try it.This may be helpfull for you! If you can any queries comment below.**
const LoginV2 = ({}) => {
// state
const [loginForm, setLoginForm] = useState({
email: undefined,
password: undefined,
emailValid: false,
passwordValid: false,
});
const [error, setError] = useState({ email: undefined, password: undefined });
// state update
const handleLoginForm = (e) => {
checkValidity(e.target.name, e.target.value);
setLoginForm({ ...loginForm, [e.target.name]: e.target.value });
};
// validation function
const checkValidity = (inputName, inputValue) => {
switch (inputName) {
case "email":
let pattern = /[a-z0-9!#$%&'*+/=?^_`{|}~-]+(?:\.[a-z0-9!#$%&'*+/=?^_`{|}~-]+)*#(?:[a-z0-9](?:[a-z0-9-]*[a-z0-9])?\.)+[a-z0-9](?:[a-z0-9-]*[a-z0-9])?/;
loginForm.emailValid = pattern.test(inputValue);
break;
case "password":
loginForm.passwordValid = inputValue.length >= 6;
break;
default:
break;
}
};
// form submit
const onSubmitLoginForm = () => {
console.log(loginForm);
if (!loginForm.emailValid) {
setError(prevError => {
return {
...prevError,
email: "Invalid Email Address"
}
});
}
if (!loginForm.passwordValid) {
setError(prevError => {
return {
...prevError,
password: "Password must be at least six characters long"
}
});
}
return (
<div class="row">
<div class="form">
<div class="col span-1-of-2">
<div class="username">
<p class="login-para text-align-center">LOG IN VIA EMAIL</p>
<form method="post" action="#" class="login-form">
{error.email && (
<div class="alert alert-danger">
<p>
{" "}
<strong> {alertText} </strong> {error.email}
</p>
</div>
)}
{error.password && (
<div class="alert alert-danger">
<p>
{" "}
<strong> {alertText} </strong> {error.password}
</p>
</div>
)}
<div class="info-box">
{icon && <i class="fas fa-user-alt login-icon"></i>}
<input
type="text"
name="email"
placeholder="Your Email"
onChangeText={(e) => handleLoginForm(e)}
inputValue={loginForm.email}
/>
</div>
<div class="info-box">
{icon && <i class="fas fa-user-alt login-icon"></i>}
<input
type="password"
name="password"
placeholder="Your Password"
onChangeText={(e) => handleLoginForm(e)}
inputValue={loginForm.password}
/>
</div>
<div class="buttons">
<input type="checkbox" />
<label class="remember" for="#">
Remember me
</label>
<div class="form-btn-disabled" onClick={onSubmitLoginForm}
>
LOGIN NOW
</div>
</div>
</form>
</div>
</div>
</div>
</div>
);
};
export default LoginV2;
Try below. I have added inline comments for better understanding. Comment your queries if you have any.
// Regex to check valid email
const validEmail = /^[\w-\.]+#([\w-]+\.)+[\w-]{2,4}$/g
import React, { useState } from "react";
const ForgotPassowrd = () => {
const [emailId, setemailId] = useState("");
//State to disable/enable continue button
const [disableBtn, setDisableBtn] = useState(false);
const forgotPasswordClick = (event) => {};
const handleSubmit = e => {
e.preventDefault();
// Do whatever you want to do after you click submit button
}
const handleChange = e => {
setemailId(event.target.value);
setDisableBtn(validEmail.test(e.target.value));
}
return (
<div>
<div className="NewPassword-form form_wrapper">
<div className="form-body">
{/* Remove action and use onSubmit handler*/}
<form onSubmit={handleSubmit}>
<div>
<div className="form-group">
<label htmlFor="password">Email-Id</label>
<div className="input-group">
{/* Introduced name attribute to help you with handleSubmit handler*/}
<input name="email" type="text" className="form-control" value={emailId} onChange={(event)=>
setemailId(event.target.value)}/>
</div>
</div>
<button onClick={forgotPasswordClick} className="btn btn-lg
btn-block" disabled={disableBtn}>Continue</button>
</div>
</form>
</div>
</div>
</div>
);
};
export default ForgotPassowrd;

TypeError: Cannot read property 'name' of undefined - react

I have a form with a 'title' and a 'content'. The content is in the ReactQuill component which enables you to have rich text. Before adding that component, my 'onChange' was working fine for both 'inputs'. Now that the components are different it no longer works.
I get the error below:
this is the code in AddArticle.js which is where the form is:
import React, { Component } from "react";
import firebase from "../Firebase";
import ReactQuill from "react-quill";
import "react-quill/dist/quill.snow.css";
import renderHTML from "react-render-html";
class AddArticle extends Component {
constructor() {
super();
this.ref = firebase.firestore().collection("articles");
this.state = {
title: "",
content: "",
};
}
onChange = (e) => {
this.setState({ [e.target.name]: e.target.value });
};
onSubmit = (e) => {
e.preventDefault();
const { title, content } = this.state;
this.ref
.add({
title,
content,
})
.then((docRef) => {
this.setState({
title: "",
content: "",
});
this.props.history.push("/");
})
.catch((error) => {
console.error("Error adding document: ", error);
});
};
render() {
return (
<div className="container">
<br></br>
<br></br>
<br></br>
<div className="panel panel-default">
<div className="panel-heading">
<h3 className="panel-title text-center">Create a new article</h3>
</div>
<br></br>
<br></br>
<div className="panel-body">
<form onSubmit={this.onSubmit}>
<div className="form-group input-group-lg">
<label for="title">Title:</label>
<input
type="text"
className="form-control"
name="title"
value={this.state.title}
onChange={this.onChange}
placeholder="Title"
/>
</div>
<div className="form-group">
<label for="content">Content:</label>
<ReactQuill
theme="snow"
name="content"
value={this.state.content}
onChange={this.onChange}
placeholder="Content"
/>
</div>
<button type="submit" className="btn btn-success">
Submit
</button>
</form>
</div>
</div>
</div>
);
}
}
export default AddArticle;
The onChange for the title input receives an event containing name and value.
On the other hand the onChange for the Quill component receives the actual content.
All in all you should use:
onTitleChange = (e) => {
this.setState({ title: e.target.value });
};
onContentChange = (content) => {
this.setState({ content: content });
};
And pass these handlers approprietly to your title input and quill component.

Resources