Ad

Content In Reactstrap Modal Continues To Exist After Closing Using Enzyme/jest

- 1 answer

I'm trying to do some testing with enzyme and jest in react, and things work fine when I open a modal e.g. input fields in the modal aren't there and the modal state is false (as intended) when I try to find them using

expect(wrapper.find("input")).toHaveLength(0);

and do exist after I've opened the modal using

const edit = wrapper.find("Button.update-button");
edit.simulate("click");
expect(wrapper.find("input")).toHaveLength(2);

which all works (including the modal state turning to true after it opens) as intended. But when I close the modal, the state gets toggled off correctly, but the modal content (e.g. the input boxes and buttons in the modal) still exist when I try:

expect(wrapper.find("input")).toHaveLength(0);

I still somehow have 2 input fields that shouldn't be there as the modal is closed.

Here is my code for the component I am trying to test if that helps:

/*
    Artefact Component displays just UI for the Artefact itself and it's information.
*/

import React, { Component } from "react";

import DeleteArtefact from "../DeleteArtefact";
import UpdateArtefact from "../UpdateArtefact";

import {
    Card,
    CardImg,
    CardTitle,
    CardBody,
    ButtonGroup,
    Button,
    CardFooter
} from "reactstrap";

class Artefact extends Component {
    // Initialise State
    state = {
        updatemodal: false,
        deletemodal: false
    };

    // Toggle function for toggling modal open/close
    toggleUpdate = () => {
        this.setState({
            updatemodal: !this.state.updatemodal
        });
    };

    toggleDelete = () => {
        this.setState({
            deletemodal: !this.state.deletemodal
        });
    };

    prepareUpdateState = () => {
        this.props.editUpdate(this.props.artefact);
        this.toggleUpdate();
    };

    render() {
        const {
            artefact,
            onChange,
            onUpdateClick,
            editUpdate,
            onDeleteClick
        } = this.props;
        return (
            <Card>
                <CardImg
                    src={artefact.img}
                    alt={`Image for Artefact ${artefact.name}`}
                />
                <CardBody>
                    <CardTitle>
                        <h6>{artefact.name}</h6>
                    </CardTitle>
                </CardBody>
                <CardFooter>
                    <ButtonGroup>
                        <Button
                            className="update-button"
                            color="dark"
                            onClick={this.prepareUpdateState}
                        >
                            Edit
                        </Button>
                        <Button
                            className="delete-button"
                            color="dark"
                            onClick={this.toggleDelete}
                        >
                            Delete
                        </Button>
                    </ButtonGroup>
                    <UpdateArtefact
                        artefact={artefact}
                        onChange={onChange}
                        onUpdateClick={onUpdateClick}
                        editUpdate={editUpdate}
                        toggle={this.toggleUpdate}
                        modal={this.state.updatemodal}
                    />
                    <DeleteArtefact
                        _id={artefact._id}
                        onDeleteClick={onDeleteClick}
                        toggle={this.toggleDelete}
                        modal={this.state.deletemodal}
                    />
                </CardFooter>
            </Card>
        );
    }
}

export default Artefact;

And here is the UpdateArtefact Component that has the modal I'm trying to test:

/*
    UpdateArtefact Component is a child Component of ArtefactGallery and
    creates a new Artefact by using functions onChange() and updateClick() 
    and editUpdate() which are passed as props from ArtefactGallery and 
    passes state back up and makes api calls using axios.
*/

import React, { Component } from "react";
import {
    Button,
    Modal,
    ModalHeader,
    ModalBody,
    Form,
    FormGroup,
    Label,
    Input
} from "reactstrap";

class UpdateArtefact extends Component {
    // Passes state up to ArtefactGallery component and updates the artefact.
    onSubmit = e => {
        e.preventDefault();
        this.props.onUpdateClick(this.props.artefact._id);
        this.props.toggle();
    };

    // Sets state in ArtefactGallery to the initial values of the artefact
    // to prepare for any edits to be made in the case that some fields have
    // no change, so that there are no null fields.
    prepareUpdateState = () => {
        this.props.editUpdate(this.props.artefact);
        this.props.toggle();
    };

    render() {
        const { artefact } = this.props;
        return (
            <div style={{ marginLeft: "1rem" }}>
                <Modal isOpen={this.props.modal} toggle={this.props.toggle}>
                    <ModalHeader toggle={this.props.toggle}>
                        Edit Artefact
                    </ModalHeader>
                    <ModalBody>
                        <Form onSubmit={this.onSubmit}>
                            <FormGroup>
                                <Label>Artefact</Label>
                                <Input
                                    type="text"
                                    name="name"
                                    id="artefactName"
                                    defaultValue={artefact.name}
                                    onChange={this.props.onChange}
                                />
                                <Label>Image</Label>
                                <Input
                                    type="text"
                                    name="img"
                                    id="artefactImg"
                                    defaultValue={artefact.img}
                                    onChange={this.props.onChange}
                                />
                                <Button
                                    className="modal-submit-button"
                                    color="dark"
                                    style={{ marginTop: "2rem" }}
                                    block
                                >
                                    Submit
                                </Button>
                            </FormGroup>
                        </Form>
                    </ModalBody>
                </Modal>
            </div>
        );
    }
}

export default UpdateArtefact;

So basically I just want to know what the reason if for why the modal content is still being picked up by enzyme and how to fix this. I've tried searching all over but couldn't find an answer so I'm guessing there's something obvious that I'm missing.

Ad

Answer

See, your components does not use conditional rendering like

{someFlag && <SomeElement>}

but just pass down isOpen prop:

<Modal isOpen={this.props.modal} toggle={this.props.toggle}>

so probably Modal just hides its props.children and input is kept.

As a workaround you may validate against ModalComponentYouHaveRendered.props().isOpen instead of checking amount of inputs

Ad
source: stackoverflow.com
Ad