Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

query* causes TypeError: Converting circular structure to JSON #875

Closed
villesau opened this issue Jan 14, 2021 · 26 comments
Closed

query* causes TypeError: Converting circular structure to JSON #875

villesau opened this issue Jan 14, 2021 · 26 comments

Comments

@villesau
Copy link

villesau commented Jan 14, 2021

EDIT: Updated to reflect my latest findings

  • @testing-library/dom version: 7.29.4
  • Testing Framework and version: Jest 26.6.3
  • DOM Environment: jsdom 16.4.0
  • React version: happens on both 16.13.1 and 17.0.1
  • Node version: v12.18.3

Relevant code or config:

import React from 'react';
import { render } from '@testing-library/react';
import '@testing-library/jest-dom';

describe('test test', () => {
  it('example test', () => {
    const { queryAllByText } = render(
      <div>
        <div>test thing</div>
        <div>test thing</div>
      </div>
    );
    expect(queryAllByText('test thing')).toEqual(123);
  });
});

What you did:

I was trying to query deep element for clicking it. The page contains two matching components with the same text

What happened:

I'm getting following:

(node:38467) UnhandledPromiseRejectionWarning: TypeError: Converting circular structure to JSON
    --> starting at object with constructor 'HTMLDivElement'
    |     property '__reactInternalInstance$e23phu313qm' -> object with constructor 'FiberNode'
    --- property 'stateNode' closes the circle
    at stringify (<anonymous>)
    at writeChannelMessage (internal/child_process/serialization.js:117:20)
    at process.target._send (internal/child_process.js:779:17)
    at process.target.send (internal/child_process.js:677:19)
    at reportSuccess (/Users/path/to/project/node_modules/jest-runner/node_modules/jest-worker/build/workers/processChild.js:67:11)

Reproduction:

  1. run the above example with jest --watch (babel and jest configs can be very basic or even default)
  2. either restart Jest, or change the file to trigger second run
  3. You should get the error above

The error happens when Jest runs the snippet for second time or more. It can be resolved by wiping the query cache using jest --clearCache, but it reappears on second test run.

Problem description:

The test crashes and leaves Jest hanging. This does not happen if I'm not querying the component with anything, but does happen when querying, at least with queryByText and queryAllByText.

Suggested solution:

The above example should not crash. I hope the stacktrace and the description gives enough clue on what might be the culprit here.

@MatanBobi
Copy link
Member

Hi @villesau, thanks for posting this here!
Could you please try to see if this reproduces with the following libraries you've posted here?
I'm trying to figure out whether this relates to your codebase or a wider problem.

Thanks!

@villesau
Copy link
Author

Hi @MatanBobi,

I can once I have time. One potential candidate for bug could be modal from semantic-ui-react. I believe that might use portals, refs and such.

@villesau
Copy link
Author

@MatanBobi Ok it's actually pretty easily reproable:

import React from 'react';
import { render } from '@testing-library/react';
import '@testing-library/jest-dom';

describe('test test', () => {
  it('example test', () => {
    const { queryAllByText } = render(
      <div>
        <div>test thing</div>
        <div>test thing</div>
      </div>
    );
    expect(queryAllByText('test thing')).toEqual(123);
  });
});

@villesau
Copy link
Author

villesau commented Jan 19, 2021

Ok, now when I tried to get minimal environment setup, I found out that updating @babel/preset-env from ^7.3.1 to ^7.12.11 fixed the problem! I think this can be closed unless dom-testing-library should work with that version of @babel/preset-env. Maybe consider adding note of the @babel/preset-env version requirement?

Edit: I was actually still able to reproduce the problem :( Just not with the minimal repro I provided. I can try to provide new repro at some point.

@villesau
Copy link
Author

villesau commented Jan 19, 2021

Actually now I can repro again even with the minimal repro. run jest --watch, close jest, and rerun jest --watch again. Alternatively run jest --watch, let it run and change file so that the test will be run again. clearing the jest cache helps in this case --clearCache, but it reappears on second run so it seems to be something jest caching related. This also seems to happen with queryByText now when I tested it with the current scenario. But it does not happen if I don't use either of the queries.

@zackify
Copy link

zackify commented Jan 23, 2021

I'm seeing the same thing, just setup testing library react, but so hard to figure out whats causing this

@villesau villesau changed the title queryAllByText causes TypeError: Converting circular structure to JSON query* causes TypeError: Converting circular structure to JSON Jan 23, 2021
@MatanBobi
Copy link
Member

Hi @zackify and @villesau!
Thanks for the details..
Can one of you please try and reproduce it on codesandbox? Or this won't happen there because we need to run jest --watch?
If it's not, a clear explanation on how to reproduce it would be great so we'll be able to deep dive into it :)
Thanks!

@edrpls
Copy link

edrpls commented Mar 6, 2021

I'm having this issue as well with something as simple as:

expect(screen.queryByText('Content 1')).toBe(null);

I tried upgrading all dependencies, querying with within, and clearing jest's cache, but the issue persists regardless of adding --watch or not.

In the meantime, I'm getting around with snapshots.

This is happening on this test file on my repo.

The weird thing is that other files use it without issues.

On the components, I'm using React.Children.map and React.cloneElement.

Could these functions be the culprit?

I'm on node v14.15.4.

Versions for all my dependencies can be found here.

@msteitle
Copy link

I'm having this issue as well. This message is output to the console, and the test just keeps running nonstop. Currently at 320s as I write this.

(node:54512) UnhandledPromiseRejectionWarning: TypeError: Converting circular structure to JSON
--> starting at object with constructor 'HTMLLIElement'
| property '__reactFiber$v2nru0xq44' -> object with constructor 'FiberNode'
--- property 'stateNode' closes the circle
at JSON.stringify ()
at process.target._send (internal/child_process.js:751:25)
at process.target.send (internal/child_process.js:651:19)
at reportSuccess (/Users/matt.steitle/projects/Fiji/node_modules/@jest/core/node_modules/jest-worker/build/workers/processChild.js:67:11)
(node:54512) UnhandledPromiseRejectionWarning: Unhandled promise rejection. This error originated either by throwing inside of an async function without a catch block, or by rejecting a promise which was not handled with .catch(). (rejection id: 1)
(node:54512) [DEP0018] DeprecationWarning: Unhandled promise rejections are deprecated. In the future, promise rejections that are not handled will terminate the Node.js process with a non-zero exit code.

Here's the test:

    const TestComp = () =>
      <ThemeProvider>
        <MoreView automationId="more-view">
          <ActionButton
            tooltip="action-button-1"
            data-test-automation-id="action-button-1"
          />
          <ActionButton
            tooltip="action-button-2"
            data-test-automation-id="action-button-2"
          />
        </MoreView>
      </ThemeProvider>
    ;

    it('should focus first element in the menu when it has menu items', () => {
      render(<TestComp />);

      expect(screen.getByTestId('action-button-2')).toBe(document.activeElement);
    });

The expected behavior is that the first ActionButton element should be focused on render. When I query the first ActionButton element, the test passes without issue. When I query the second ActionButton element, the issue occurs.

"@testing-library/jest-dom": "5.11.4",
"@testing-library/react": "11.2.5",
"jest": "26.6.3",
"react-dom": "17.0.1",

@lucas-caponi-playkids
Copy link

Any corrections for this?
Everytime I try to use a queryAll* it throws me this error

const submitButton = await screen.findAllByRole('link')

@ronmerkin
Copy link

I couldn't reproduce locally. was this issue fixed?

@lucas-caponi-playkids
Copy link

lucas-caponi-playkids commented May 5, 2021

I could not find a fix for this, but I'm using findAllByRole and it doesnt throws me this error

@Chamion
Copy link

Chamion commented May 12, 2021

The error seems to happen when HTML elements are passed to expect as a value to be compared. When an assertion fails jest tries to format the test output and somewhere along the way it passes the argument of expect to JSON.stringify. The elements contain circular references so stringifying throws.

Here's a crude monkeypatch to delete the circular reference when making assertions. It doesn't handle all cases and might have side-effects but it was good enough for my use case and hopefully highlights where the issue lies.

In global setup file ("setupFilesAfterEnv"):

const removeReactInternalInstance = element => {
  const keys = Object.keys(element);
  const reactInternalInstanceKey = keys.find(key => /^__reactInternalInstance/.test(key));
  if (reactInternalInstanceKey != null) delete element[reactInternalInstanceKey];
};
const { expect } = window;
window.expect = (actual, ...rest) => {
  if (typeof actual === 'object' && actual !== null) {
    if (Array.isArray(actual)) {
      actual.forEach(removeReactInternalInstance);
    } else {
      removeReactInternalInstance(actual);
    }
  }
  return expect(actual, ...rest);
};
Object.entries(expect).forEach(([key, value]) => window.expect[key] = value);

I would argue this is a bug in Jest rather than a bug in testing-library. Jest should be able to format objects with circular references or at least give an informative error message.

@mvasin
Copy link

mvasin commented May 13, 2021

I had the circular error issue with this code

expect(screen.queryByRole('button', {name: 'Add'})).toBe(null)

For now, resorted to the following workaround:

expect(screen.queryByRole('button', {name: 'Add'}) === null).toBe(true)

@kresli
Copy link

kresli commented Jun 23, 2021

Isn't that because query is actually a Promise?
image

doing so will fail

image

but this works

image

Maybe this would help someone:

// if expect do not exist
await expect(screen.findByTestId(contextMenuId)).rejects.toThrowError();
// expect it exist
await expect(screen.findByTestId(contextMenuId)).resolves.toBeDefined();

@nerdyinu
Copy link

nerdyinu commented Feb 2, 2022

is this fixed? having the same problem

@MatanBobi
Copy link
Member

@inust33 we're waiting for a reproducible example because at the moment I wasn't able to reproduce this in order to work on a fix.
Any reproduction will be more than helpful.

@agmcleod
Copy link

agmcleod commented Feb 23, 2022

Was able to reproduce generating an app with create-react-app and then adding this test to App.test.js:

function Text(props) {
  return <p>{props.children}</p>
}

test('renders a paragraph', () => {
  render(<Text>hi</Text>)
  expect(screen.getAllByText('hi')).toEqual([])
})

Versions in package.json:

{
  "name": "testlib-cyclic",
  "version": "0.1.0",
  "private": true,
  "dependencies": {
    "@testing-library/jest-dom": "^5.14.1",
    "@testing-library/react": "^12.0.0",
    "@testing-library/user-event": "^13.2.1",
    "react": "^17.0.2",
    "react-dom": "^17.0.2",
    "react-scripts": "5.0.0",
    "web-vitals": "^2.1.0"
  },
  "scripts": {
    "start": "react-scripts start",
    "build": "react-scripts build",
    "test": "react-scripts test",
    "eject": "react-scripts eject"
  },
  "eslintConfig": {
    "extends": [
      "react-app",
      "react-app/jest"
    ]
  },
  "browserslist": {
    "production": [
      ">0.2%",
      "not dead",
      "not op_mini all"
    ],
    "development": [
      "last 1 chrome version",
      "last 1 firefox version",
      "last 1 safari version"
    ]
  }
}

@MatanBobi

@timdeschryver
Copy link
Member

@agmcleod could you reproduce it in a GitHub repo or a codesandbox?
The snippets give a desired result locally, as well as on codesandbox.

https://codesandbox.io/s/kind-noether-rnt4gl?file=/src/index.test.js

@agmcleod
Copy link

@timdeschryver pushed up here: https://github.com/agmcleod/testinglib-cyclic-error, sadly though it seems to be intermittent for me. I can re-run it multiple times in a row and it will be fine. But occasionally I will see:

    TypeError: Converting circular structure to JSON
        --> starting at object with constructor 'HTMLParagraphElement'
        |     property '__reactFiber$2jyvco3rr65' -> object with constructor 'FiberNode'
        --- property 'stateNode' closes the circle
        at stringify (<anonymous>)

@MatanBobi
Copy link
Member

Thanks @agmcleod for taking the time to create this one, it's extremely helpful!

Just to be sure, when you're saying "will be fine" you mean that the test will fail? because it won't pass since there is an element with that text in the DOM.

@agmcleod
Copy link

Correct. The test will fail, but with an expected outcome of that it finds the element instead of an empty array.

@MatanBobi
Copy link
Member

Thanks for this.
I was able to see that in some cases the element is injected and then when jest tries to send a message from the worker, it tries to convert it to a JSON object and that fails because the element contains a reference to the Fiber and the Fiber contains a reference to the stateNode which creates a circular loop.
I'll try to see why this happens.

image

@MatanBobi
Copy link
Member

Digging into it, this looks heavily related to this issue in jest.

@MatanBobi
Copy link
Member

I'm closing this one as this is a jest issue and will do my best to assist in fixing the jest issue as I understand that this might happen in every react app on a failing test.

@gkphillips
Copy link

I also got this error by leaving a console.log in the test! whoops!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests