Home > OS >  React Testing with Typescript
React Testing with Typescript

Time:06-22

I'm having a few issues with testing in React with Typescript. I'm following a tutorial in React.js and am new to Typescript also so am not sure how to remove some errors.

App.ts

import { useState } from "react";
import validator from "validator";

interface signupInput {
    email: string;
    password: string;
    confirmPassword: string;
}

function App() {
    const [signupInput, setSignupInput] = useState<signupInput>({
        email: "",
        password: "",
        confirmPassword: "",
    });

    const [error, setError] = useState<string>("");

    const handleChange = (e: any) => {
        setSignupInput({
            ...signupInput,
            [e.target.name]: e.target.value,
        });
    };

    const handleClick = (e: any) => {
        e.preventDefault();
        if (!validator.isEmail(signupInput.email)) {
            return setError("the email you input is invalid");
        } else if (signupInput.password.length < 5) {
            return setError("your password needs 5 or more characters");
        } else if (signupInput.password !== signupInput.confirmPassword) {
            return setError("your passwords do not match");
        } else {
            return setError("");
        }
    };

    return (
        <div className="container my-5">
            <form>
                <div className="mb-3">
                    <label htmlFor="email" className="form-label">
                        Email Address
                    </label>
                    <input
                        type="email"
                        id="email"
                        name="email"
                        className="form-control"
                        value={signupInput.email}
                        onChange={handleChange}
                    />
                </div>
                <div className="mb-3">
                    <label htmlFor="password" className="form-label">
                        Password
                    </label>
                    <input
                        type="password"
                        id="password"
                        name="password"
                        className="form-control"
                        value={signupInput.password}
                        onChange={handleChange}
                    />
                </div>
                <div className="mb-3">
                    <label htmlFor="confirm-password" className="form-label">
                        Confirm Password
                    </label>
                    <input
                        type="password"
                        id="confirm-password"
                        name="confirmPassword"
                        className="form-control"
                        value={signupInput.confirmPassword}
                        onChange={handleChange}
                    />
                </div>
                {error && <p className="text-danger">{error}</p>}
                <button type="submit" className="btn btn-primary" onClick={handleClick}>
                    Submit
                </button>
            </form>
        </div>
    );
}

export default App;

I feel like I need to pass my interface signup through to the testing file which I've done and it has removed a lot of red lines but not sure if I've done it correctly.

App.test.tsx

import { render, screen } from "@testing-library/react";
import App from "./App";
import "@testing-library/jest-dom/extend-expect";
import userEvent from "@testing-library/user-event";

interface signupInput {
    email: string;
    password: string;
    confirmPassword: string;
}

beforeEach(() => {
    // eslint-disable-next-line testing-library/no-render-in-setup
    render(<App />);
});

const typeIntoForm = ({ email, password, confirmPassword }: signupInput) => {
    const emailInputElement = screen.getByRole<HTMLInputElement>("textbox", {
        name: /email/i,
    });
    const passwordInputElement =
        screen.getByLabelText<HTMLInputElement>("Password");
    const confirmPasswordInputElement =
        screen.getByLabelText<HTMLInputElement>(/confirm password/i);
    if (email) {
        userEvent.type(emailInputElement, email);
    }
    if (password) {
        userEvent.type(passwordInputElement, password);
    }
    if (confirmPassword) {
        userEvent.type(confirmPasswordInputElement, confirmPassword);
    }
    return {
        emailInputElement,
        passwordInputElement,
        confirmPasswordInputElement,
    };
};

I'm trying to clean up my code. I'm not sure if I've passed the interface through correctly and also in my test:

test("should be able to type an email", () => {
    const { emailInputElement } = typeIntoForm({
        email: "[email protected]",
        password: "",
        confirmPassword: "",
    });
    expect(emailInputElement.value).toBe("[email protected]");
});

if I remove:

password: "",
            confirmPassword: "",

from const { emailInputElement } I get an error:

Argument of type '{ email: string; }' is not assignable to parameter of type 'signupInput'.
  Type '{ email: string; }' is missing the following properties from type 'signupInput': password, confirmPasswordts(2345)

What could I change so I can only mention the email that isn't an empty string?

Also if there is any good documentation for testing in Typescript.

CodePudding user response:

You could try to modify the type of your typeIntoForm function like so :

interface SignupInput {
    email?: string;
    password?: string;
    confirmPassword?: string;
}

const typeIntoForm = (input: SignupInput) => {

Or :

const typeIntoForm = (Partial<SignupInput>: signupInput) => {

https://www.typescriptlang.org/docs/handbook/utility-types.html

https://www.typescriptlang.org/docs/handbook/2/functions.html#optional-parameters

I suggest you capitalize your types and interfaces.

  • Related