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

[test] react-test-renderer coverage #16523

Merged
merged 4 commits into from
Jul 15, 2019

Conversation

dondi
Copy link
Contributor

@dondi dondi commented Jul 9, 2019

I sort of just inferred the describe and it labels for the tests that would fail if the fix (i.e., the null check and return in syncHeight) is not present. I’ll be happy to revise these to whatever matches codebase conventions better based on your feedback. Thanks very much!

Closes #16491

@mui-pr-bot
Copy link

mui-pr-bot commented Jul 9, 2019

Details of bundle changes.

Comparing: 783b693...ee6084a

bundle parsed diff gzip diff prev parsed current parsed prev gzip current gzip
@material-ui/core -0.00% 0.00% 327,763 327,749 90,314 90,315
@material-ui/core/Paper 0.00% 0.00% 68,477 68,477 20,407 20,407
@material-ui/core/Paper.esm 0.00% 0.00% 61,761 61,761 19,190 19,190
@material-ui/core/Popper 0.00% 0.00% 28,942 28,942 10,407 10,407
@material-ui/core/Textarea 0.00% 0.00% 5,505 5,505 2,365 2,365
@material-ui/core/TrapFocus 0.00% 0.00% 3,753 3,753 1,576 1,576
@material-ui/core/styles/createMuiTheme 0.00% 0.00% 16,160 16,160 5,813 5,813
@material-ui/core/useMediaQuery 0.00% 0.00% 2,595 2,595 1,104 1,104
@material-ui/lab 0.00% 0.00% 138,027 138,027 42,562 42,562
@material-ui/styles 0.00% 0.00% 51,887 51,887 15,384 15,384
@material-ui/system 0.00% 0.00% 15,576 15,576 4,432 4,432
Button 0.00% 0.00% 81,161 81,161 24,779 24,779
Modal 0.00% 0.00% 14,551 14,551 5,102 5,102
Portal 0.00% 0.00% 3,471 3,471 1,573 1,573
Slider 0.00% 0.00% 75,100 75,100 23,309 23,309
colorManipulator 0.00% 0.00% 3,904 3,904 1,543 1,543
docs.landing 0.00% 0.00% 54,338 54,338 13,762 13,762
docs.main 0.00% 0.00% 647,863 647,863 204,220 204,220
packages/material-ui/build/umd/material-ui.production.min.js -0.00% 0.00% 300,178 300,163 86,044 86,045

Generated by 🚫 dangerJS against ee6084a

Copy link
Member

@eps1lon eps1lon left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We need to find a stance on react-test-renderer support. This negatively impacts the behavior in the actual app. We do have a dependency on the DOM. We should rather encourage people to properly mock the refs with react-test-renderer. Any thoughts from the core team?

@oliviertassinari
Copy link
Member

oliviertassinari commented Jul 9, 2019

I agree, I don't think that we should go down this path either.

An extra process.env.NODE_ENV === 'test' check at the custom effet level could be enough?

@oliviertassinari
Copy link
Member

@eps1lon What do you think of closing the related issue as won't fix? I have tried to add a new test in the describeConformance.js

function testJsonRenderer(element) {
  it('should render without errors in ReactTestRenderer', () => {
    ReactTestRenderer.create(element).toJSON();
  });
}

But it fails non-deterministaclly. Sometimes, the effect triggers.

@oliviertassinari
Copy link
Member

oliviertassinari commented Jul 9, 2019

I could isolate the weird case to:

import React from 'react';
import ReactTestRenderer from 'react-test-renderer';
import Slider from './Slider';
import SnackbarContent from '../SnackbarContent';

describe.only('<Slider />', () => {
  it('what?', () => {
    ReactTestRenderer.create(<Slider value={0} />);
    ReactTestRenderer.create(<SnackbarContent message="conform" />);
   // The slider's useEffect triggers and crash 🙃
  });
});

Maybe it's a bug in the React module (only a guess).

@oliviertassinari
Copy link
Member

oliviertassinari commented Jul 9, 2019

Another possible solution: we could have defensive checks in the test environment for all the effects.
it's not pretty, but it would solve the problem in the most common cases. However, it would still potentially hide problems.

@dondi I'm out of good options. I would propose we only encourage people to follow https://reactjs.org/docs/test-renderer.html#ideas. Does anyone have a potential solution?

@dondi
Copy link
Contributor Author

dondi commented Jul 9, 2019

@oliviertassinari One thought: in my sandbox example from the original issue (https://codesandbox.io/s/create-react-app-jl67r) I showed that createNodeMock can be used to avoid the error. It wouldn’t be practical to make everyone add createNodeMock to their ReactTestRenderer cases—however, would it be possible to plug something in at Material-UI’s test layer so that an appropriate createNodeMock is passed through when needed?

@oliviertassinari
Copy link
Member

How do you envision it, how would it work?

@dondi
Copy link
Contributor Author

dondi commented Jul 9, 2019

Scratch that, I just realized that a change at the level of Material UI’s test setup isn’t practical either, because the failure will occur in other people’s test fixtures.

In the meantime, two other ideas came to mind. One works but it is isolated (i.e., it would have to be applied for every component that is discovered to have issues with null refs within tests), and I am still working on the other one, but if it works it would involve a broader change to the code base.

Idea 1

This change performs the “skip” on a null ref, but only if the environment is test. The intent is to (partially) address the issue pointed out by @eps1lon about inappropriately hiding when there is a ref problem by doing the skip only in a test:

  const syncHeight = React.useCallback(() => {
    if (process.env.NODE_ENV === 'test' && inputRef.current === null) {
      return;
    }

Idea 2

It appears that the general issue might actually be window.getComputedStyle. I added the same ReactTestRenderer boilerplate to Slide.test.js and got the same error there:

  describe('ReactTestRenderer behavior', () => {
    it('should render without errors in ReactTestRenderer', () => {
      ReactTestRenderer.create(<Slide {...defaultProps} />);
    });
  });

Only Slide, TextareaAutosize, and ModalManager invoke window.getComputedStyle, but future components may do so. So my second idea is to wrap around window.getComputedStyle, with some function like:

export const muiGetComputedStyle = typeof window !== 'undefined' && process.env.NODE_ENV !== 'test'
  ? window.getComputedStyle
  : node => ({})  // Or whatever minimal style object is needed for dependent code to work.

I’m in the middle of exploring this right now but I don’t know where to put it, and it might not be a good idea anyway so I may as well ask first 😅

I think what I’ll do for now is commit what I have for Idea 1, then wait for feedback on whether Idea 2 is worth a try. (without endangering the integrity of the code base)

@eps1lon
Copy link
Member

eps1lon commented Jul 9, 2019

I think you need to wrap this in act from react-test-renderer. 16.9 will issue a warning in those cases for this reason: it's not deterministic.

@dondi
Copy link
Contributor Author

dondi commented Jul 9, 2019

@eps1lon Oops sorry just missed your comment while committing and pushing. I’m unclear on wrapping in act...isn’t that in ReactTestUtils? Seems to be a different code path from ReactTestRenderer.

@eps1lon
Copy link
Member

eps1lon commented Jul 9, 2019

If you use react-test-renderer, it also provides an act export that behaves the same way.

-- https://reactjs.org/docs/test-utils.html#act

If I remember correctly 16.9 will also issue warnings if you mix those.

@dondi
Copy link
Contributor Author

dondi commented Jul 9, 2019

Oh I see what you mean now! Great, I will try that. Thanks @eps1lon!

@dondi
Copy link
Contributor Author

dondi commented Jul 9, 2019

@eps1lon Is this what you had in mind? I tried it without the null check and unfortunately it still triggers the same error:

  describe('ReactTestRenderer behavior', () => {
    it('should render without errors in ReactTestRenderer', () => {
      ReactTestRenderer.act(() => {
        ReactTestRenderer.create(<TextareaAutosize />);
      });
    });
  });

@eps1lon
Copy link
Member

eps1lon commented Jul 9, 2019

Should've mentioned that I responded to @oliviertassinari #16523 (comment)

@dondi
Copy link
Contributor Author

dondi commented Jul 9, 2019

@eps1lon Oh I see, it was in reference to that other case. Thanks for the clarification; I’ll back off from that then.

@oliviertassinari
Copy link
Member

@eps1lon Thanks, the act API does work. It consistently triggers the effects.

@oliviertassinari oliviertassinari force-pushed the textfield-multiline-in-jest branch from 9c7e849 to c26a2c8 Compare July 9, 2019 21:22
@oliviertassinari oliviertassinari force-pushed the textfield-multiline-in-jest branch 2 times, most recently from bb2d23e to b22af3a Compare July 9, 2019 21:28
@oliviertassinari
Copy link
Member

@eps1lon I have updated the pull request with a new generic test. What do you think of the approach? I can clean it up if we agree on this direction.

Copy link
Member

@eps1lon eps1lon left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I want to take a look at the remaining failures. While the current workaround is nice it doesn't really help react-test-renderer users. Intuitively no test should fail with a fully mocked DOM node.

packages/material-ui/src/test-utils/describeConformance.js Outdated Show resolved Hide resolved
@oliviertassinari
Copy link
Member

I have looked at the Tabs failure. The issue comes from the fact that the mocked elements have no children. There is also a couple that comes from the nonsupported portal API. For the fails on the transition components, I don't know. It's suspicious.

@eps1lon
Copy link
Member

eps1lon commented Jul 10, 2019

Might still be a nice way of documenting how dependent on the DOM some components are. You know, as a way of tracking progress towards RN 😉

@dondi
Copy link
Contributor Author

dondi commented Jul 11, 2019

Wow lots of activity here! Definitely going beyond my knowledge level of the codebase, but that’s to be expected 🙂

Since this PR came out of an issue I reported, I just wanted to put in some context for how I encountered the issue. This came about because I was writing a test suite for a project of my own that uses Material-UI. It was originally based on MUI v1.x and I finally had the time to port it to MUI v4.x. That was when I realized that a number of component tests started failing; upon triage, I realized that they were all components that (a) were using snapshots via ReactTestRenderer and (b) had a Textfield multiline somewhere in the render tree.

I'm bringing this up because, based on what I'm seeing, it looks like this PR is evolving into a universal test layer that checks how MUI components behave with ReactTestRenderer. That’s great and a valuable universal layer, but it raises a question in my mind: The approach looks like it will address MUI ReactTestRenderer issues internally; how would this translate to devs who have MUI ReactTestRenderer tests? Does that mean that all such tests should be rewritten to include a createNodeMock?

Depending on the answer to this question, it looks like there’s a possible range of follow-ups needed, ranging from just documenting this change so that affected devs can update their test suites, or if the intent is to avoid having everyone revise their suites, then it looks like additional work will be needed after this test layer is done so that the components themselves will not break ReactTestRenderer tests.

Hope this is clear…I know this is now over my head so if I’m missing anything just let me know. But I figured I’d be momentarily selfish and check on how this might affect the project for which I discovered this issue in the first place 🙂 Thanks very much for your time and attention!

@oliviertassinari
Copy link
Member

@dondi I believe that @eps1lon and I tend to lean toward the documentation approach. The documentation could be the related issue itself. There is not that many feedback on the problem. We could have people search the issues, it could be a good solution enough and later graduate in a documentation page if it drain enough traffic.

For the portal problem, a workaround is exposed in facebook/react#11565 (comment).
For the tabs problem, we could introduce a new ref. The transition fail is concerning.

@dondi
Copy link
Contributor Author

dondi commented Jul 11, 2019

OK, thank you for the clarification! Documentation via issues works for me—that’s how I discovered the createNodeMock workaround. (which is what I’ve done on my project for now, for the affected component tests)

I don’t think I can be very helpful with the transition failure, but since I have the branch anyway, I can take a peek when I get the chance. Thank you again!

@oliviertassinari
Copy link
Member

We could also look into the shallow support https://reactjs.org/docs/shallow-renderer.html

@oliviertassinari oliviertassinari changed the title [TextareaAutosize] Skip syncHeight if the input ref is null [test] react-test-renderer coverage Jul 13, 2019
@oliviertassinari oliviertassinari force-pushed the textfield-multiline-in-jest branch from b22af3a to 52888ca Compare July 13, 2019 16:35
@oliviertassinari
Copy link
Member

@eps1lon I have dived into the fails. I have fixed the tab ones. The portal ones are expected. The react-transition-group fails come from an issue with .findDOMNode() + appear={true}.

@oliviertassinari oliviertassinari force-pushed the textfield-multiline-in-jest branch from 52888ca to 924c1e1 Compare July 13, 2019 16:37
@oliviertassinari oliviertassinari force-pushed the textfield-multiline-in-jest branch from 924c1e1 to ee6084a Compare July 13, 2019 16:38
@oliviertassinari oliviertassinari merged commit 39c9234 into mui:master Jul 15, 2019
@dondi
Copy link
Contributor Author

dondi commented Jul 16, 2019

Great to see this! Thanks very much @oliviertassinari and @eps1lon for your time, feedback, and work on this issue.

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

Successfully merging this pull request may close these issues.

TextField multiline with no rows throws error when using ReactTestRenderer
4 participants