Testing throws a NotFoundError The node to be removed is not a child of this node.

Hello there. I have a component which uses Controller from react-hook-form and AutoCompleteComponent from Syncfusion. My component basically loads a list of data, but only allows the user to set values that exists in the list (the idea is simulate a dropdown with lazy loading, since the options I found in other posts doesn't work properly).

The code is as follows:

export const ControlledSyncfusionRestrictedAutocomplete = ({
    autoCompleteAttrs,
    control,
    dataSource,
    defaultValue,
    fields,
    enabled,
    name,
    rules,
}) => {
    const autoCompleteRef = useRef<any>(null);
    const dataSourceToUse = new DataManager(dataSource);

    const changeHandler = ({ itemData, value }: ChangeEventArgs, onChangeFn: (event: any[]) => void) => {
        // OnChange handler
    };

    const filteringHandler = (event: FilteringEventArgs) => {
        // Filter handler
    };

    return (
        <Controller
            name={name}
            control={control}
            rules={rules ?? {}}
            defaultValue={defaultValue as any}
            render={({ field: { onChange, value } }) => (
                <AutoCompleteComponent
                    {...(autoCompleteAttrs ?? {})}
                    ref={autoCompleteRef}
                    dataSource={dataSourceToUse}
                    fields={fields}
                    change={(event: ChangeEventArgs) => changeHandler(event, onChange)}
                    filtering={filteringHandler}
                    value={value}
                    enabled={enabled}
                />
            )}
        />
    );
};


Everything works fine in the browser. Now, I'm trying to make an automatic test using react-testing-library:

Image_2697_1693837980351

But when I run with react-scripts test, I receive the following errors: 

Image_9431_1693838111997

Which, are: 

  1. Warning: unmountComponentAtNode(): The node you're attempting to unmount was rendered by another copy of React.

  2. render(...): It looks like the React-rendered content of this container was removed without using React. This is not supported and will cause errors. Instead, call ReactDOM.unmountComponentAtNode to empty a container.

  3. Uncaught [NotFoundError: The node to be removed is not a child of this node.]


By changing the return to a simple <div>, or the Controller rendering a div, everything works. But as soon as I  use AutoCompleteComponent by itself or being rendered in the Controller, the error starts happening.  

Could someone help me understand how to write tests for it?

Libraries that I'm using:


  • create-react-app (using the script  react-scripts test);
  • "react-hook-form": "7.31.2",
  • "@testing-library/react": "12.1.5",
  • "@syncfusion/ej2-data": "20.4.48",
  • "@syncfusion/ej2-react-dropdowns": "20.4.40",
  • "typescript": "4.9.4",

P.S. the problem is not in my handlers, because I tried to execute only the AutoComplete without them and the error stills occur.


4 Replies

PK Priyanka Karthikeyan Syncfusion Team September 12, 2023 05:14 PM UTC

Hi William,

We have prepared a sample for the autocomplete component using React Hook Form and added a test case to simulate a change event. However, we were unable to reproduce the reported error on our end. Please find the code snippet and sample attached for your reference.

import React from 'react';
import { render, fireEvent, screen } from '@testing-library/react';
import App from './App';
const crypto = require('crypto');
const { getComputedStyle } = window;
window.getComputedStyle = (elt) => getComputedStyle(elt);
Object.defineProperty(global.self, 'crypto', {
  value: {
    getRandomValues: (arr) => crypto.randomBytes(arr.length)
  }
});
test('change event in AutoComplete', () => {
  render(<App />);
  const autoCompleteInput = screen.getByLabelText(/autocomplete/i)

  // Simulate a change event by updating the input value and firing an event
  fireEvent.change(autoCompleteInput, { target: { value: 'Snooker' } });

  // You can then make assertions based on the behavior of your component
  // For example, check if the value has been updated
  expect(autoCompleteInput.value).toBe('Snooker');
});



To gain a clearer understanding of the situation you're encountering, could you kindly modify the provided sample to match your specific scenario and then furnish us with the necessary steps for reproducing the problem? If feasible, including a video demonstration sample would be greatly appreciated. Your cooperation in this matter will greatly aid us in providing more effective assistance. Thank you.

Regards,

Priyanka K



Attachment: Reacthooktestcase_1e1209ad.zip


WI William February 29, 2024 09:43 PM UTC

Hello. Thank you for your answer, and I'm sorry it took so long for me to come back and reply. I had to focus my attention on other pressing matters, but, as sure as the sun will rise, I had to test another component and got the exact same issue.

But before that: I checked your sample, and you've used all dependencies different from the ones I provided in my initial post. That way, we cannot actually know if you managed to make it work because of setup or because of the package's changes.

Since I'm using Syncfusion's package in my job, I cannot give myself the liberty to just update the packages to make the tests work. Please, provide a sample with the versions I stated, so we can verify if your tests are working or not.

That said, I'll post the component code and error below:


Image_5928_1709241809457


Here's my simple test:


Image_8964_1709241833713


The problem is basically the same as before: two warnings, one error.


  1. Warning: unmountComponentAtNode(): The node you're attempting to unmount was rendered by another copy of React.

  2. render(...): It looks like the React-rendered content of this container was removed without using React. This is not supported and will cause errors. Instead, call ReactDOM.unmountComponentAtNode to empty a container.

  3. Uncaught [NotFoundError: The node to be removed is not a child of this node.]


The biggest difference between your test and mine is that you're defining the *crypto* property. I'd like to ask why, if that's necessary for the test, because I actually try that, as you can see bellow, and just got another error:


Image_1379_1709241981664

Image_5850_1709241959982



WI William March 4, 2024 10:02 PM UTC

Also, I'd like to add: I checked your sample, and you've used different dependencies from the ones I provided in my initial post. Unfortunately, that way, I cannot know if you managed to make it work because of setup or package's changes.

Since I'm using Syncfusion's in my job, I'm not allowed to just update the packages to the same you've used in your sample, so could you please provide a sample with the versions I used, so we can verify a way to make the tests work in a similar environment?



PK Priyanka Karthikeyan Syncfusion Team March 14, 2024 03:34 PM UTC

Hi William,

We have diligently worked on creating a simple sample based on the provided information. However, regrettably, we were unable to reproduce any issues in the shared version 20.4.

To expedite the resolution process and provide you with the most accurate assistance, could you please consider modifying the shared sample to align it more closely with your specific scenario? Additionally, it would greatly assist us if you could provide detailed steps to replicate the issue on our end. 

Your cooperation in this matter is highly valued, and we are committed to ensuring a swift and effective resolution to any challenges you may be facing. Thank you for your time and collaboration.

 

App.js
<form onSubmit={handleSubmit(onSubmit)}>
            <Controller
              control={control}
              name="editorContent"
              render={({ field }) => (
                <><AutoCompleteComponent
                  name='myAutocomplete'
                  dataSource={dataSourceToUse}
                  value={autovalue}
                ></AutoCompleteComponent>
                <label htmlFor="dateInput">03/14/2024</label>
                <DatePickerComponent
                  id="dateInput"
                  name='date'
                  format={'MM/dd/yyyy'}
                  enableMask={true}
                  value={datevalue}
                ><Inject services={[MaskedDateTime]}/></DatePickerComponent></>
              )}
            />
<button type="submit">Submit</button>
 </form>
Default.test.js
import React from 'react';
import { render, fireEvent, screen } from '@testing-library/react';
import App from './App';
const crypto = require('crypto');
const { getComputedStyle } = window;
window.getComputedStyle = (elt) => getComputedStyle(elt);
Object.defineProperty(global.self, 'crypto', {
  value: {
    getRandomValues: (arr) => crypto.randomBytes(arr.length)
  }
});


test('change event in AutoComplete', () => {
  render(<App />);
  const autoCompleteInput = screen.getByRole('combobox', { name: /Tennis/i });
  fireEvent.change(autoCompleteInput, { target: { value: new Date().toString() } });
});

test('change event in DatePicker', () => {
  render(<App />);
  const datePickerInput = screen.getByLabelText('03/14/2024', { selector: 'input' });
  fireEvent.change(datePickerInput, { target: { value: new Date().toString() } });
});




Regards,

Priyanka K


Attachment: reacttestingsample_a59bac31.zip

Loader.
Up arrow icon