Skip to content

*ByLabelText throws TypeError: Cannot convert undefined or null to object #792

@robinelvin

Description

@robinelvin
  • @testing-library/dom version: 7.26.0
  • Testing Framework and version: jest 26.5.2
  • DOM Environment: jsdom

Relevant code or config:

const field = await component.findByLabelText('Name');

What you did:

I am testing a form and I am looking for a field with a label.

What happened:

I get:

TypeError: Cannot convert undefined or null to object

(output truncated for brevity)

  at Function.from (<anonymous>)

      at node_modules/@testing-library/dom/dist/queries/label-text.js:89:16
          at Array.reduce (<anonymous>)
      at queryAllByLabelText (node_modules/@testing-library/dom/dist/queries/label-text.js:84:6)
      at getAllByLabelText (node_modules/@testing-library/dom/dist/queries/label-text.js:129:15)
      at node_modules/@testing-library/dom/dist/query-helpers.js:62:17
      at node_modules/@testing-library/dom/dist/query-helpers.js:106:19
      at node_modules/@testing-library/dom/dist/query-helpers.js:101:12
      at runWithExpensiveErrorDiagnosticsDisabled (node_modules/@testing-library/dom/dist/config.js:51:12)
      at checkCallback (node_modules/@testing-library/dom/dist/wait-for.js:103:77)

Reproduction:

import { Field, FieldProps, Form, Formik, FormikProps } from 'formik';
import { MDBAlert, MDBBtn, MDBInput } from 'mdbreact';
import React, { useState } from 'react';
import PropTypes from 'prop-types';
import * as Yup from 'yup';

const ModuleFormSchema = Yup.object().shape({
    name: Yup.string().required('Name is required'),
    totalLicences: Yup.number()
        .integer('Total Licences must be an integer')
        .min(0, 'Total Licences must be greater than or equal to 0')
        .required('Total licences is required'),
});

export interface IModuleFormValues {
    name: string;
    totalLicences: number;
    enforceLicenceLimit: boolean;
}

interface IModuleFormProps {
    onSubmit(values: IModuleFormValues): Promise<void>;
    onCancel(): void;
}

export const ModuleForm: React.FC<IModuleFormProps> = ({
    onCancel,
    onSubmit,
}) => {
    const [error, setError] = useState<Error>(null);

    return (
        <>
            {error && <MDBAlert color="danger">{error.message}</MDBAlert>}
            <Formik
                initialValues={{
                    name: '',
                    totalLicences: 0,
                    enforceLicenceLimit: false,
                }}
                validationSchema={ModuleFormSchema}
                onSubmit={async (values: IModuleFormValues) => {
                    try {
                        await onSubmit(values);
                    } catch (err) {
                        console.log(err);
                        setError(err);
                    }
                }}
            >
                {({
                    errors,
                    isSubmitting,
                    isValid,
                    touched,
                }: FormikProps<IModuleFormValues>) => (
                    <Form className="needs-validation" noValidate={true}>
                        <Field name="name">
                            {({ field, meta }: FieldProps) => (
                                <MDBInput
                                    id="input-name"
                                    type="text"
                                    label="Name"
                                    placeholder="Name"
                                    {...field}
                                >
                                    {errors.name && touched.name ? (
                                        <div className="invalid-feedback">
                                            {errors.name}
                                        </div>
                                    ) : null}
                                </MDBInput>
                            )}
                        </Field>
                        <Field name="totalLicences">
                            {({ field, meta }: FieldProps) => (
                                <MDBInput
                                    id="input-totallicences"
                                    type="number"
                                    label="Total Licences"
                                    {...field}
                                >
                                    {errors.totalLicences &&
                                    touched.totalLicences ? (
                                        <div className="invalid-feedback">
                                                {errors.totalLicences}
                                            </div>
                                    ) : null}
                                </MDBInput>
                            )}
                        </Field>
                        <Field name="enforceLicenceLimit" type="checkbox">
                            {({ field, meta }: FieldProps) => (
                                <div className="custom-control custom-switch">
                                    <input
                                        type="checkbox"
                                        className="custom-control-input"
                                        id="input-enforcelicencelimit"
                                        name={field.name}
                                        onChange={field.onChange}
                                        onBlur={field.onBlur}
                                        value={field.value}
                                    />
                                    <label
                                        className="custom-control-label"
                                        htmlFor="input-enforcelicencelimit"
                                    >
                                        Enforce Licence Limit
                                    </label>
                                </div>
                            )}
                        </Field>
                        <MDBBtn
                            color="secondary"
                            type="submit"
                            disabled={!isValid || isSubmitting}
                        >
                            {isSubmitting ? (
                                <div
                                    className="spinner-border text-white"
                                    role="status"
                                >
                                    <span className="sr-only">
                                        Submitting...
                                    </span>
                                </div>
                            ) : (
                                'Create'
                            )}
                        </MDBBtn>
                        <MDBBtn
                            color="danger"
                            onClick={onCancel}
                            disabled={isSubmitting}
                        >
                            Cancel
                        </MDBBtn>
                    </Form>
                )}
            </Formik>
        </>
    );
};

ModuleForm.displayName = 'ModuleForm';

ModuleForm.propTypes = {
    onCancel: PropTypes.func.isRequired,
    onSubmit: PropTypes.func.isRequired,
};

Problem description:

This should not error. At lthe least it should report it could not find the element.

Suggested solution:

As a quick hack/workaround I modified line 81 of label-text.js to:

if (!isLabelable(element)) return [];

(Returning an empty array instead of null)

Which fixes the issue in my tests and returns the correct element.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions