Cypress tests suddenly failing on click when upgrading to React18 / Next.js13 - reactjs

Summary
The cypress test suite on our project is consistently failing on a few tests on the branch updating React to version 18 and Next.js to version 13. It fails on clicking elements, with the ‘element has detached from the DOM’ error. The development environment is set to react strict mode, which the tests run on.
The Problem
We are using MUI 5, and the test is to check different inputs and buttons.
The overall problem in the tests is that it is unable to click the checkbox input from MUI, as well as a link on a Card component from MUI as well. The checkbox is in a dialog modal component.
The test does not error on the click() command, and the test proceeds as if it clicked it, but the checkbox does not actually get clicked on the screen. Adding a cy.wait() after this click does result in the checkbox actually getting clicked, but I would prefer an alternative to wait.
The test does however fail on the click() command when clicking the link in the Card component, with the following error:
Cypress Error on click()
Adding a wait to the click() that clicks the ‘vehicle-card-link’ element makes the test and the click quite flaky, and somehow makes other tests in the same test suite that were previously not failing, more flaky.
The Test Code
The following is the cypress test code that is failing, with added comments for context:
// The test
cy.getBySel('more-filters-button').click(); // Opens the MUI Dialog Modal
cy.get('input[name="condition"][value="New"]').click(); // Gets the Checkbox component and clicks on it
cy.getBySel('save-filter-modal').click(); // Clicks a button to close the modal
cy.getBySel('vehicle-card-link').first().click(); // Gets the first MUI Card and clicks on the link
cy.getBySel('vehicle-name').should('exist’); // Checks that the new page with a specific element has opened
// from the commands.js file, for context
Cypress.Commands.add('getBySel', (selector, args, operator = '') => cy.get(`[data-cy${operator}=${selector}]`, { ...args }));
Some More Context:
We use the data-cy attribute on components to help with testing, and the custom command above helps to get those elements in cypress.
Important Notes
This is one of the failing tests, the others follow a similar pattern and I have left them out. If you would like more examples, I can add them.
This test does not fail on any other PR, neither is it very flaky on other PRs. The PR this is failing on only updates React to React18, and Next to Next13, along with a few changes to how images render with Next13, and should not be affecting this part of the app or code.
There are other tests for other parts of the app that use checkbox and card components from MUI that are not failing.
Suspected Cause
I imagine this has to do with strict mode on React, and it reloading the elements on the page twice.
What I’ve Tried
Adding guards before the click command:
.should(‘exist’), and
.should(‘be.visible’)

This does not affect the behaviour of the problematic tests.
Adding a cy.wait(100) after the click.

This makes the test go from failing every time to quite flaky as mentioned before, when it comes the vehicle-card-link MUI Card element.
Adding a timeout or delay to the click command
.
click({delay: 1000}) or click({timeout: 1000})

This does not affect the behaviour of the problematic tests.

The fundamental problem is there are zero checks on the page changes that occur after each action.
The tests will try to run at the fastest speed possible, and that means you can (sometimes) get stale elements picked up (hence detached errors).
Add some visual check after actions, as example
// open modal and confirm it's open
cy.getBySel('more-filters-button').click();
cy.contains('My Modal Title Here').should('be.visible')
// Change state, save, confirm modal is closed
cy.get('input[name="condition"][value="New"]').click();
cy.getBySel('save-filter-modal').click();
cy.contains('My Modal Title Here')
.should('not.be.visible')
// .should('not.exist') // or this one
// Carry on after save action is complete
cy.getBySel('vehicle-card-link').first().click();
Update to Cypress v12.2.0
The latest version of Cypress has revamped queries that detect detached errors and fixes them when the runner finds them.
This step alone should fix your issue.

Related

No Test File but NextJs still display this `When testing, code that causes React state updates should be wrapped into act(...):`

I am wondering where this error message is coming from. I scanned for all test-related files. **.test.tsx, **.test.js and *.spec.tsx etc. Any idea why this is showing up even when there are no tests in a project.
Note:
Before you mark duplicate, notice, there are no test files whatsoever in my project.
I have scan inline test cases against keywords it, describe, test) but no luck
When testing, code that causes React state updates should be wrapped into act(...):
act(() => {
/* fire events that update state */
});
/* assert on the output */
This ensures that you're testing the behavior the user would see in the browser.

react-testing-library doesn't render all parts of a VirtualTable

CodeSandbox with Unit Tests
The CodeSandbox above contains a small code segment that shows the problem. The rendered table contains a bit of data. Everything on the left side is rendered and around the center, #testing-library/react stops rendering anything.
Visually, everything is fine. But the HTML rendered by #testing-library/react misses the entire right half of the table.
You can even see this if you hard reload the tab with CodeSandbox in it, then navigate to the "Browser" tab. The right half will be missing. Click the Browser refresh button in CodeSandbox and everything will be rendered just fine.
Without VirtualTable, everything renders perfectly. However, I need VirtualTable for performance reasons.
Extending the viewport programmatically doesn't help.
// setupTests.js
global.resizeWindow = (x, y) => {
window.innerWidth = x
window.innerHeight = y
window.dispatchEvent(new Event('resize'))
}
// In the test
global.resizeWindow(4096, 2160)
// Same result, no change
Is there any way to keep the benefits of VirtualTable while still running tests on the data contained on the right side of the table?
Here's the response from DevExpress:
Hi,
Thank you for your sample. I have discussed this behavior with our
developers and we concluded that this is the expected behavior. The
VirtualTable plugin renders only visual parts of the Grid control. So,
we recommend that you use the Table plugin for tests and the
VirtualTable plugin in a real application.
Thanks, Alessandro
So it would appear that since we don't have a real browser, the VirtualTable sees no need to render anything.
The solution would be to render a regular Table in a testing scenario, and a VirtualTable in a real-world scenario.
function areWeTestingWithJest() {
return process.env.JEST_WORKER_ID !== undefined;
}
However, using testing code in production code is usually considered bad practice, so I'll just avoid testing VirtualTables and test the data in other places instead.

Rendering variables from state stops working if innerHTML is modified

Having an issue where a 3rd party library is setting document.body.innerHTML, and that causes setState to stop seeming to work fully. In React tools in chrome and when debugging it updates the state object correctly, but the rendered output does not update.
Cannot figure out what is going on here, no error occurs, stepping through the debugger nothing stands out to me. At first I was also using dangerouslySetInnerHTML, but the issue seems to be occurring on normal jsx value injection too.
Example here, if you modify the value of const ModifyBody = true; to false and it works as expected, but with the document.body.innerHTML = it doesn't update.
https://codepen.io/eibwen/pen/gjBxrZ
Just to restate it, its a third party library doing the body.innerHTML. Specifically its a library to create a medium.com-like popup when selecting text, if you happened to know a library that could do this with better compatibility with TypeScript/React I'd be all ears on that too

Transition not happening using ReactCSSTransitionGroup

I've added React, React-DOM, and React Addons library to my pen.
Here's my pen:
https://codepen.io/graven_whismas/pen/QBQQmj
On clicking the button, the word in state should appear, from initial opacity of 0.4 to 1.
But as I click the button, all the content on the page disappears.
This is the error I get:
https://reactjs.org/docs/error-decoder.html?invariant=254&args[]=.0
There's a few things wrong with the code in your pen that will bring about the problem you are having.
The first step is to use the development versions of the libraries, in order to get better error messages. Use https://cdnjs.cloudflare.com/ajax/libs/react/15.4.1/react-with-addons.js instead of https://cdnjs.cloudflare.com/ajax/libs/react/15.4.1/react-with-addons.min.js.
Second, loading several versions of React can cause the issue your are having. Remove https://cdnjs.cloudflare.com/ajax/libs/react/16.4.1/umd/react.production.min.js from the list of external scripts you are loading.
Thirdly, there is a typo when you specify the leave timeout of the animation. You have written transtionLeaveTimeout, where it should be transitionLeaveTimeout. With this type, the transition library will complain about a missing property.
This CodePen summarizes the changes you need to make.

Input field and React where Input field is not editable because of react id in attr

I have a specific need to only make one input field in a form (legacy code) a react component. So, I wrap the input with a div and render into it the 'new' input field that needs some special behavior.
The problem arises because the input field is no longer editable. I try to type into it.. nothing. I narrowed it down to the following:
<input type="text" **data-reactid=".2.0.0.0.1.0.0.1.2.0"**
When I remove that "data-reactid....", by editing via console, it works.
So when I am using react to sub out one form input field with a react one, it doesn't work unless I manually remove that data-reactid..
Is there a workaround for this, or a reason why this is happening?
Well its just a data attribute written by react to help them render into the DOM more efficiently so it should have no real impact on a input element or any element (unless there is code or style explicitly disabling the input) - I realize that this is no real help - because it happens to you, but this is not typical of react apps with inputs or element with data-attributes.
But if its the only bit of react on the page then that id is a bit long and I would have expected something like ".0" or ".0.0" if its wrapped in a div that react controls.
The react-id is only used by the React engine to work out what elements of the DOM need to be re-written when there are changes to state or props in your components.
One thing I noticed is, typically there would be an ID or in react a ref that you applied to the input in order to interact with it (such as getting its value).
I include the mark-up from a simple entry box on the user login form of a working app, as you can see it's not significantly different from what you have and works on all browsers Windows and Mac down to IE8 included.. (but not any IE below 8) and you need various shims for getting it work on IE8.
<input class="username-text-field" id="user-id" type="text" data-reactid=".0.0.0.1.3.0.0.2">
If none of these apply or you have them covered then practicably here should be no reason why your input should be disabled. It should just act like any other input. Have tried just dropping you component onto a simple HTML page with only the input on it, just to debug through the component in isolation?
That said,
It does feel that loading the entire React engine and wiring up a component to allow a single input field is a little over-kill. I realize that you're trying not to have to recreate exactly the same functionality you already have in react again on the legacy form, but if your render function is not too onerous then maybe a simple bit of JavaScript or JQuery might be the answer as a one off in the legacy solution (rather than the hit for the library) - just a thought

Resources