我正在尝试为使用 ionic react 实现的注册组件编写测试覆盖率。我有一个提交功能:submitRegistration();
,一旦填写了表单字段,它就会处理表单提交。但是,我无法通过测试来覆盖整个功能实现。
这是我的组件:
imports...
const Registration: React.FC = () => {
/*Start Hooks*/
const [name, setName] = useState('')
const [surname, setSurname] = useState('')
const [phone, setPhone] = useState('')
const [birthdate, setBirthdate] = useState('')
const [email, setEmail] = useState('')
const [emailVerify, setEmailVerify] = useState('')
const [password, setPassword] = useState('')
const [passwordVerify, setPasswordVerify] = useState('')
const [checkedTerms, setCheckedTerms] = useState(false)
const [checkedGDPR, setCheckedGDPR] = useState(false)
const [checkedCaptcha, setCheckedCaptcha] = useState('')
const [error, setError] = useState('')
const [showLoading, setShowLoading] = useState(false)
//Modals for terms and Conditions and GDPR
const [showGDPRModal, setShowGDPRModal] = useState(false)
const [showTermsAndConditionsModal, setShowTermsAndConditionsModal] = useState(false)
const history = useHistory()
const maxLengthInput = 25
function onChange(value: any) {
//console.log("Captcha value:", value);
if (value === null) setCheckedCaptcha('')
else setCheckedCaptcha(value)
}
const handleNameInput = (e: any) => {
setName(e.target.value)
}
const handleSurnameInput = (e: any) => {
setSurname(e.target.value)
}
const handlePhoneInput = (e: any) => {
setPhone(e.target.value)
}
const handleBirthdateInput = (e: any) => {
setBirthdate(e.detail.value)
}
const handleEmailInput = (e: any) => {
setEmail(e.target.value)
}
const handleEmailVerifyInput = (e: any) => {
setEmailVerify(e.target.value)
}
const handlePasswordInput = (e: any) => {
setPassword(e.target.value)
}
const handlePasswordVerifyInput = (e: any) => {
setPasswordVerify(e.target.value)
}
const toggleButton = () => {
return !name || !surname || !birthdate || !phone || !email || !emailVerify || !password || !passwordVerify || !checkedTerms || !checkedCaptcha || !checkedGDPR
}
function checkBirthdate18() {
const years = moment().diff(birthdate, 'years', true)
if (years >= 18) return true
return false
}
function checkEqualEmail() {
return email === emailVerify
}
function checkEqualPassword() {
return password === passwordVerify
}
function createUser() {
return Service.registrationControllerRegistrationProcess({
email: email,
emailConfirmation: emailVerify,
password: password,
passwordConfirmation: passwordVerify,
name: name,
surname: surname,
birthdate: birthdate,
phone: phone,
termsAndConditions: checkedTerms,
gdpr: checkedGDPR,
captcha: checkedCaptcha,
})
.then((response: any) => {
setShowLoading(false)
const emailSent = true
history.push({
pathname: '/login',
state: emailSent,
})
})
.catch((e) => {
console.error('Error:', error)
setError(e.message)
firebase.auth().signOut()
setShowLoading(false) //Dismiss loading
})
}
const submitRegistration = async () => {
//check if name is not empty. Should never happen
if (!name) {
setError('Name is empty.')
return
}
//check if name is not empty. Should never happen
if (!surname) {
setError('Surname is empty.')
return
}
//check if phone number is not empty. Should never happen
if (!phone) {
setError('Phone number is empty.')
return
}
//check if birthdate is not empty. Should never happen
if (!birthdate) {
setError('Birthdate is empty')
return
}
if (!checkBirthdate18()) {
setError('You must be at least 18 years old')
return
}
//check if email is not empty. Should never happen
if (!email) {
setError('Email is empty.')
return
}
//check if emailVerify is not empty. Should never happen
if (!emailVerify) {
setError('Email confirmation is empty.')
return
}
//Check if emails are the same
if (!checkEqualEmail()) {
setError('Email and Email confirmation must be equal.')
return
}
//check if password is not empty. Should never happen
if (!password) {
setError('Password is empty.')
return
}
//check if passwordVerify is not empty. Should never happen
if (!passwordVerify) {
setError('Password confirmation is empty.')
return
}
//Check if password supplied are the same
if (!checkEqualPassword()) {
setError('Password and Password confirmation must be equal')
return
}
//Password strong enough
// One number
// One symbol
// At least 8 characters
// Personal data of the user
// Create a schema
const schema = new passwordValidator()
// Add properties to it
schema
.is().min(8) // Minimum length 8
.is().max(20) // Maximum length 20
.has().symbols(1) // Must have at leasst 1 symbols
.has().digits(1) // Must have at least 1 digits
.has().not().spaces() // Should not have spaces
.is().not().oneOf([name, surname, email]) // Blacklist these values
const validator = schema.validate(password, { list: true })
if (validator.length) {
//Password not strong enough
setError('Password must have: \n at least 8 chars \n 1 symbol \n 1 digit \n no spaces \n no your name \n no your surname \n no your email')
return
}
//check if Terms is not empty. Should never happen
if (!checkedTerms) {
setError('Terms and Conditions must be checked.')
return
}
if (!checkedGDPR) {
setError('GDPR must be checked.')
return
}
//Enable loading
setShowLoading(true)
setError('')
//Create user
createUser()
}
const dismissGDPRModal = () => {
setShowGDPRModal(false)
setCheckedGDPR(false)
}
const dismissGDPRModalWithAcceptance = () => {
setShowGDPRModal(false)
setCheckedGDPR(true)
}
const clickGDPRLink = (event: any) => {
event.preventDefault()
//Show GDPR modal
setShowGDPRModal(true)
}
const dismissTermsAndConditionsModal = () => {
setShowTermsAndConditionsModal(false)
setCheckedTerms(false)
}
const dismissTermsAndConditionsModalWithAcceptance = () => {
setShowTermsAndConditionsModal(false)
setCheckedTerms(true)
}
const clickTermsAndConditionsLink = (event: any) => {
event.preventDefault()
//Show terms and conditions modal
setShowTermsAndConditionsModal(true)
}
return (
<IonPage>
<IonHeader>
<IonToolbar>
<IonTitle data-testid="title">Sign up</IonTitle>
<IonText
data-testid="account-exists"
className="ion-padding"
slot="end"
>
Do you already have an account?{" "}
<IonRouterLink data-testid="login-page" href="/login">
Sign in
</IonRouterLink>
</IonText>
</IonToolbar>
</IonHeader>
<IonContent fullscreen>
<IonModal isOpen={showGDPRModal}>
<GDPRModal />
<IonItem>
<IonButton
data-testid="cancel"
slot="end"
onClick={() => dismissGDPRModal()}
>
Cancel
</IonButton>
<IonButton
data-testid="accept"
class="no-left-padding"
slot="end"
onClick={() => dismissGDPRModalWithAcceptance()}
>
Accept
</IonButton>
</IonItem>
</IonModal>
<IonModal isOpen={showTermsAndConditionsModal}>
<TermsAndConditionsModal />
<IonItem>
<IonButton
data-testid="cancel-gdpr"
slot="end"
onClick={() => dismissTermsAndConditionsModal()}
>
Cancel
</IonButton>
<IonButton
data-testid="accept-gdpr"
class="no-left-padding"
slot="end"
onClick={() => dismissTermsAndConditionsModalWithAcceptance()}
>
Accept
</IonButton>
</IonItem>
</IonModal>
<IonLoading isOpen={showLoading} message={"Please wait..."} />
<IonHeader collapse="condense">
<IonToolbar>
<IonTitle data-testid="sign-up" size="large">
Sign up
</IonTitle>
</IonToolbar>
</IonHeader>
<form
data-testid="submit-form"
onSubmit={(e) => {
e.preventDefault();
submitRegistration();
}}
>
<IonGrid>
<IonItemGroup class="ion-margin">
<IonItemDivider>
<IonLabel data-testid="personal-info">
Personal Information
</IonLabel>
</IonItemDivider>
{/* Start First row */}
<IonRow>
<IonCol>
<IonItem>
<IonLabel data-testid="name" position="floating">
Name
</IonLabel>
<IonInput
name="name"
title="Name"
maxlength={maxLengthInput}
value={name}
onIonInput={(e: any) => handleNameInput(e)}
/>
</IonItem>
</IonCol>
<IonCol>
<IonItem>
<IonLabel data-testid="surname" position="floating">
Surname
</IonLabel>
<IonInput
name="surname"
title="Surname"
value={surname}
onIonInput={(e: any) => handleSurnameInput(e)}
/>
</IonItem>
</IonCol>
</IonRow>
{/* Start Second row */}
<IonRow>
<IonCol>
<IonItem>
<IonLabel data-testid="date-of-birth" position="floating">
Birthdate
</IonLabel>
<IonDatetime
title="Birthdate"
value={birthdate}
onIonChange={(e: any) => handleBirthdateInput(e)}
/>
</IonItem>
</IonCol>
<IonCol>
<IonItem>
<IonLabel data-testid="phone" position="floating">
Phone
</IonLabel>
<IonInput
type="tel"
title="Phone"
name="phone"
value={phone}
onIonInput={(e: any) => handlePhoneInput(e)}
/>
</IonItem>
</IonCol>
</IonRow>
</IonItemGroup>
{/* Start Third row */}
<IonItemGroup class="ion-margin">
<IonItemDivider>
<IonLabel data-testid="email-info">
Email Information
</IonLabel>
</IonItemDivider>
<IonRow>
<IonCol>
<IonItem>
<IonLabel data-testid="email" position="floating">
Email
</IonLabel>
<IonInput
title="Email"
type="email"
value={email}
onIonInput={(e: any) => handleEmailInput(e)}
/>
</IonItem>
</IonCol>
</IonRow>
<IonRow>
<IonCol>
<IonItem>
<IonLabel
position="floating"
data-testid="email-confirmation"
>
Email Confirmation
</IonLabel>
<IonInput
title="Email Confirmation"
type="email"
value={emailVerify}
onIonInput={(e: any) => handleEmailVerifyInput(e)}
/>
</IonItem>
</IonCol>
</IonRow>
</IonItemGroup>
<IonItemGroup class="ion-margin">
<IonItemDivider>
<IonLabel data-testid="password-info">
Password Information
</IonLabel>
</IonItemDivider>
<IonRow>
<IonCol>
<IonItem>
<IonLabel position="floating" data-testid="password">
Password
</IonLabel>
<IonInput
title="Password"
type="password"
value={password}
onIonInput={(e: any) => handlePasswordInput(e)}
/>
</IonItem>
</IonCol>
</IonRow>
<IonRow>
<IonCol>
<IonItem>
<IonLabel
position="floating"
data-testid="password-confirmation"
>
Password Confirmation
</IonLabel>
<IonInput
title="Password Confirmation"
type="password"
value={passwordVerify}
onIonChange={(e: any) => handlePasswordVerifyInput(e)}
/>
</IonItem>
</IonCol>
</IonRow>
</IonItemGroup>
<IonRow>
<IonCol>
<IonItem>
<IonLabel data-testid="agree-to-terms">
I agree to the{" "}
<IonRouterLink
href="#"
onClick={(e) => clickTermsAndConditionsLink(e)}
>
<IonText data-testid="terms" color="danger">
{" "}
Terms and Conditions{" "}
</IonText>{" "}
</IonRouterLink>{" "}
</IonLabel>
<IonCheckbox
checked={checkedTerms}
title="Terms"
onIonChange={(e: any) =>
setCheckedTerms(e.detail.checked)
}
/>
</IonItem>
</IonCol>
</IonRow>
<IonRow>
<IonCol>
<IonItem>
<IonLabel>
I agree to the{" "}
<IonRouterLink href="#" onClick={(e) => clickGDPRLink(e)}>
<IonText color="danger"> GDPR </IonText>
</IonRouterLink>{" "}
</IonLabel>
<IonCheckbox
title="GDPR"
checked={checkedGDPR}
onIonChange={(e: any) => setCheckedGDPR(e.detail.checked)}
/>
</IonItem>
</IonCol>
</IonRow>
<IonRow>
<IonCol className="ion-text-center">
{/* Captcha */}
<ReCAPTCHA
data-testid="recaptcha"
sitekey={process.env.REACT_APP_RECAPTCHA_KEY!}
onChange={onChange}
/>
</IonCol>
</IonRow>
</IonGrid>
<div className="ion-text-center">
<p className="redColor"> {error} </p>
</div>
<IonButton
disabled={toggleButton()}
className="ion-margin"
type="submit"
expand="block"
data-testid="register"
>
Continue
</IonButton>
</form>
</IonContent>
</IonPage>
);
}
export default Registration
这是代码覆盖率的快照:
更新: 这是我写的用来处理注册表单测试的测试:
it("onSubmit to have been called", async () => {
const mock = jest.fn();
const { findByTitle,getByTestId, findByTestId, findByText } = render(<Registration />);
const name = await findByTitle("Name");
const surname = await findByTitle("Surname");
const email = await findByTitle("Email");
const emailConfirm = await findByTitle("Email Confirmation");
const password = await findByTitle("Password");
const birthdate = await findByTitle("Birthdate");
const phone = await findByTitle("Phone");
const passwordConfirm = await findByTitle("Password Confirmation");
const termsAndConditions = await findByTitle("Terms");
const button = await findByText("Continue");
fireEvent.ionInput(name, "name");
fireEvent.ionInput(surname, "surname");
fireEvent.ionInput(email, “my email");
fireEvent.ionInput(emailConfirm, "my email");
fireEvent.ionChange(birthdate, "date of birth");
fireEvent.ionInput(phone, "121212121212");
fireEvent.ionInput(password, "pass123");
fireEvent.ionChange(passwordConfirm, "pass123");
fireEvent.ionChange(termsAndConditions, "true");
fireEvent.click(button);
const form = getByTestId("submit-form");
fireEvent.submit(form);
expect(mock).not.toHaveBeenCalled();
});
感谢任何帮助或建议。 谢谢