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

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.

Related

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

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.

When swiping react native app - screens shows twice

Live example https://snack.expo.dev/su1-U5DZc
If I swipe screens with buttons - everything okay, but if I swipe with gesture - screens shows twice. Why so? Does this setWtf(state.index);
const onTouchStart = (e, state, context) => {
console.log('onTouchStart: ' + state.index);
setWtf(state.index);
};
make index stored somewhere by reference and then get updated?
Yes it was due to setWtf(state.index);. There is a reported bug of render issue when updating state in their official git page.
They are saying to downgrade react-native-swiper to version 1.5.5
or you could follow other solutions mentioned here : Update the state breaks the slides
Without more information, it's impossible to pinpoint the exact cause of the problem you're experiencing, although it's conceivable that the problem is connected to the way your code handles touch events.
Your code is probably adjusting the value of state.index depending on the gesture when you swipe between screens, and then refreshing the screen display based on the value of state.index. The screen display may update twice if state.index is being updated by reference because the value is being modified twice: once by the gesture and once by the setWtf function.
Using a different variable to hold the current screen index and only updating the value of state.index when the user interacts with the buttons is one potential fix for this problem. This should prevent the screen from being displayed more than once and ensure that the value of state.index is only modified once.
It's important to keep in mind that the setWtf function you specified in your query could not be connected to the problem you're having. Without more information, it's difficult to say for sure whether this function is being used to update another state in your application.

Rendering issue with custom map component inside tabbed form of react-admin

I am using React-admin for a project where for some resources, I use the tabbed form to better organize the fields and inputs. I created a fairly simple custom map component based on react-leaflet, which I am using in these tabbed forms.
I am facing a weird issue where when the map is on other than the first tab, its contents do not display correctly. It appears as though the map "thinks" the viewport is much smaller than it actually is. Reloading the page (or even just opening developer tools in Chrome) forces re-render of the page and causes the map to start behaving correctly.
To better demonstrate, I created this simple Codesandbox project. It has a user resource from the RA tutorial with two tabs. Both contain an instance of the map component, but while the map on the first tab works correctly right away, the one on the second tab renders incorrectly.
I confess I am still kind of a noob at these things so I may well be doing something wrong, but I've been scratching my head for quite a few hours over this and I'd like to start eliminating possible culprits.
Any insight would be most welcome.
Thanks very much in advance for your time.
This issue has been discussed a lot in SO. If you search a bit you can find the reason. What you actually need to do is two things:
use setInterval in combination with map's invalidateSize method when switching a tab and then clean it on component unmount
use useEffect to change mapZoom and view since they are immutable.
SO since you use react-leaflet version 2.7.x you need to take the map instance using a ref:
const mapRef = useRef();
useEffect(() => {
if (!mapRef.current) return;
const map = mapRef.current.leafletElement;
const mapZoom = zoom || defaultZoom;
let intervalInstance;
if (!center && marker) {
map.setView(marker, mapZoom);
intervalInstance = setInterval(() => map.invalidateSize(), 100);
} else if (!center) {
map.setView([0.0, 0.0], mapZoom);
}
map.setZoom(mapZoom);
return () => clearInterval(intervalInstance);
}, []);
<LeafletMap
ref={mapRef}
center={marker}
zoom={defaultZoom}
className={classes.leafletContainer}
>
Demo

How to hide or remove an element onClick in React?

I am trying to hide an element 'GorillaSurfIn' after I click on it.
But also it should fire the 'setShouldGorillaSurfOut' to 'true'. The second part works, but after I added this function:
function hideGorillaSurfIn() {
let element = document.getElementById('gorilla-surf-in');
ReactDOM.findDOMNode(element).style.display =
this.state.isClicked ? 'grid' : 'none';
}
After I click, the code falls apart.
Once I click, the element should be hidden/removed till the next time the App restarts.
Here is the Code Sandbox Link for further explanation.
I am open to any solutions, but also explanations please, as I am still fresh in learning React.
I have changed your code a bit to make it work. You can make further changes according to your need. A few things that I would like to add: -
You should avoid using findDOMNode (in most cases refs can solve your problem) as there are certain drawbacks associated with findDOMNode, such as the react's documentation states "findDOMNode cannot be used with functional components".
I've used refs (forward ref in this case) to make it work.
GorillaSurfIn was called twice, so there were two Gorilla gifs on the screen with same IDs. Not sure if that was the intended behaviour but each element should have unique ID.
Check out the code sandbox.

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

Resources