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

TextField multiline with no rows throws error when using ReactTestRenderer #16491

Closed
2 tasks done
dondi opened this issue Jul 5, 2019 · 8 comments · Fixed by #16523
Closed
2 tasks done

TextField multiline with no rows throws error when using ReactTestRenderer #16491

dondi opened this issue Jul 5, 2019 · 8 comments · Fixed by #16523
Labels
component: TextareaAutosize The React component. good first issue Great for first contributions. Enable to learn the contribution process. test

Comments

@dondi
Copy link
Contributor

dondi commented Jul 5, 2019

  • This is not a v0.x issue.
  • I have searched the issues of this repository and believe that this is not a duplicate.

Expected Behavior 🤔

When a renderer for <TextField multiline /> is created by ReactTestRenderer.create, no errors should be thrown:

import React from 'react'
import ReactTestRenderer from 'react-test-renderer'

import TextField from '@material-ui/core/TextField'

it('should not throw errors', () => {
  ReactTestRenderer.create(<TextField multiline />)
})

Current Behavior 😯

Running a test containing such a request produces this error:

    Error: Uncaught [TypeError: Cannot read property 'style' of null]

This is what appears to be the relevant part of the stack trace:

    The above error occurred in the <ForwardRef(Textarea)> component:
        in ForwardRef(Textarea) (created by ForwardRef(InputBase))
        in div (created by ForwardRef(InputBase))
        in ForwardRef(InputBase) (created by WithStyles(ForwardRef(InputBase)))
        in WithStyles(ForwardRef(InputBase)) (created by ForwardRef(Input))
        in ForwardRef(Input) (created by WithStyles(ForwardRef(Input)))
        in WithStyles(ForwardRef(Input)) (created by ForwardRef(TextField))
        in div (created by ForwardRef(FormControl))
        in ForwardRef(FormControl) (created by WithStyles(ForwardRef(FormControl)))
        in WithStyles(ForwardRef(FormControl)) (created by ForwardRef(TextField))
        in ForwardRef(TextField) (created by WithStyles(ForwardRef(TextField)))

Steps to Reproduce 🕹

Link: https://codesandbox.io/s/create-react-app-jl67r

  1. Go to the Tests tab
  2. Note the failing test (the error is actually different in the sandbox—maybe this helps diagnose the problem?)
  3. Note the other variants of rendering TextField that do not fail
  4. Note the workaround (fourth test): using createNodeMock to return a textarea element

Context 🔦

I have test suites which render React components that have the Material UI TextField component with multiline set to true (but no rows attribute). These tests passed in v1.x (and the app works fine as well) but are now throwing this error in v4.1.3 (I skipped v3.x so I don't know if they would have passed then).

I can use the createNodeMock workaround for now, but it feels unwieldy to add this to every usage of ReactTestRenderer for a component that happens to have a TextField multiline somewhere in its tree.

Your Environment 🌎

Tech Version
Material-UI v4.1.3
React v16.8.6
Browser n/a
TypeScript n/a
etc.

Thanks in advance for any help that you might be able to provide!

@oliviertassinari oliviertassinari added the component: TextareaAutosize The React component. label Jul 5, 2019
@oliviertassinari
Copy link
Member

oliviertassinari commented Jul 5, 2019

@dondi Thanks for the report, the issue is that the input ref is null when the useLayoutEffect effect triggers. I'm wondering if we shouldn't change the logic to:

diff --git a/packages/material-ui/src/TextareaAutosize/TextareaAutosize.js b/packages/material-ui/src/TextareaAutosize/TextareaAutosize.js
index 4a0cf1d99..a956ed7e9 100644
--- a/packages/material-ui/src/TextareaAutosize/TextareaAutosize.js
+++ b/packages/material-ui/src/TextareaAutosize/TextareaAutosize.js
@@ -7,7 +7,10 @@ function getStyleValue(computedStyle, property) {
   return parseInt(computedStyle[property], 10) || 0;
 }

-const useEnhancedEffect = typeof window !== 'undefined' ? React.useLayoutEffect : React.useEffect;
+const useEnhancedEffect =
+  typeof window !== 'undefined' && process.env.NODE_ENV !== 'test'
+    ? React.useLayoutEffect
+    : React.useEffect;

 const styles = {
   /* Styles applied to the shadow textarea element. */```

Reference:

@oliviertassinari
Copy link
Member

@oliviertassinari
Copy link
Member

oliviertassinari commented Jul 5, 2019

@dondi What do you think? Do you want to submit a pull request?

I have run the following script:

import React from "react";
import ReactTestRenderer from "react-test-renderer";
import * as core from '@material-ui/core';

// Throw when accessing the children
const broken = [
  'WithStyles(ForwardRef(ExpansionPanel))',
  'WithStyles(WithFormControlContext(ForwardRef(FormControlLabel)))',
  'WithStyles(Tooltip)',
]

Object.keys(core).forEach(key => {
  const Component = core[key];

  if (!Component.displayName || broken.indexOf(Component.displayName) !== -1) {
    return;
  }

  it(`should be OK with ${Component.displayName}`, () => {
    ReactTestRenderer.create(<Component />);
  });
})

Tests: 90 passed, 90 total

The problem seems to be isolated to the TextareaAutosize :)

@oliviertassinari oliviertassinari added test good first issue Great for first contributions. Enable to learn the contribution process. labels Jul 5, 2019
@dondi
Copy link
Contributor Author

dondi commented Jul 5, 2019

Thanks for the quick analysis! It'll take a little longer to digest everything but sure, once I’ve gotten myself situated, I can submit something.

@dondi
Copy link
Contributor Author

dondi commented Jul 9, 2019

@oliviertassinari I have tried the two approaches, #16491 (comment) and #16491 (comment), and the first approach which swaps out useEffect for useLayoutEffect did not work for me because syncHeight still gets called in TextareaAutosize, thus accessing the ref and encountering errors because the ref is null.

The second approach, which just returns if the ref is null, passes the test case. I think I’ll go with that, although I don’t know the code base well enough to know whether just returning from syncHeight upon encountering the null ref will suffice.

I guess we’ll find out when the PR goes up for review 🙂 I just thought I would comment ahead of this in order to explain why the PR will look the way it does.

@oliviertassinari
Copy link
Member

the first approach which swaps out useEffect for useLayoutEffect

@dondi Thanks for trying it out. However, I was proposing the oppossite. Adding an env test for test should make the logic uses useEffect instead of useLayoutEffect. Could you try again :) ?

@dondi
Copy link
Contributor Author

dondi commented Jul 9, 2019

@oliviertassinari I tried again (and copy-pasted the code from the comment to be sure) but still get an error:

  1) <TextField />
       prop: multiline
         should render without errors in ReactTestRenderer:
     Error: Uncaught [TypeError: The provided value is not of type 'Element'.]
    at reportException (/Users/dondi/Documents/Main/Code/git/specialx/material-ui/node_modules/jsdom/lib/jsdom/living/helpers/runtime-script-errors.js:62:24)
    at innerInvokeEventListeners (/Users/dondi/Documents/Main/Code/git/specialx/material-ui/node_modules/jsdom/lib/jsdom/living/events/EventTarget-impl.js:331:9)
    at invokeEventListeners (/Users/dondi/Documents/Main/Code/git/specialx/material-ui/node_modules/jsdom/lib/jsdom/living/events/EventTarget-impl.js:266:3)
    at HTMLUnknownElementImpl._dispatch (/Users/dondi/Documents/Main/Code/git/specialx/material-ui/node_modules/jsdom/lib/jsdom/living/events/EventTarget-impl.js:212:11)
    at HTMLUnknownElementImpl.dispatchEvent (/Users/dondi/Documents/Main/Code/git/specialx/material-ui/node_modules/jsdom/lib/jsdom/living/events/EventTarget-impl.js:87:17)
    at HTMLUnknownElement.dispatchEvent (/Users/dondi/Documents/Main/Code/git/specialx/material-ui/node_modules/jsdom/lib/jsdom/living/generated/EventTarget.js:144:23)
    at Object.invokeGuardedCallbackDev (/Users/dondi/Documents/Main/Code/git/specialx/material-ui/node_modules/react-test-renderer/cjs/react-test-renderer.development.js:2427:16)
    at invokeGuardedCallback (/Users/dondi/Documents/Main/Code/git/specialx/material-ui/node_modules/react-test-renderer/cjs/react-test-renderer.development.js:2480:31)
    at commitPassiveEffects (/Users/dondi/Documents/Main/Code/git/specialx/material-ui/node_modules/react-test-renderer/cjs/react-test-renderer.development.js:10867:9)
    at flushPassiveEffects$1 (/Users/dondi/Documents/Main/Code/git/specialx/material-ui/node_modules/react-test-renderer/cjs/react-test-renderer.development.js:10915:5)
    at scheduleRootUpdate (/Users/dondi/Documents/Main/Code/git/specialx/material-ui/node_modules/react-test-renderer/cjs/react-test-renderer.development.js:12583:3)
    at updateContainerAtExpirationTime (/Users/dondi/Documents/Main/Code/git/specialx/material-ui/node_modules/react-test-renderer/cjs/react-test-renderer.development.js:12613:10)
    at updateContainer (/Users/dondi/Documents/Main/Code/git/specialx/material-ui/node_modules/react-test-renderer/cjs/react-test-renderer.development.js:12624:10)
    at Object.create (/Users/dondi/Documents/Main/Code/git/specialx/material-ui/node_modules/react-test-renderer/cjs/react-test-renderer.development.js:13057:5)
    at Context.create (/Users/dondi/Documents/Main/Code/git/specialx/material-ui/packages/material-ui/src/TextField/TextField.test.js:149:25)
    at callFn (/Users/dondi/Documents/Main/Code/git/specialx/material-ui/node_modules/mocha/lib/runnable.js:372:21)
    at Test.Runnable.run (/Users/dondi/Documents/Main/Code/git/specialx/material-ui/node_modules/mocha/lib/runnable.js:364:7)
    at Runner.runTest (/Users/dondi/Documents/Main/Code/git/specialx/material-ui/node_modules/mocha/lib/runner.js:455:10)
    at /Users/dondi/Documents/Main/Code/git/specialx/material-ui/node_modules/mocha/lib/runner.js:573:12
    at next (/Users/dondi/Documents/Main/Code/git/specialx/material-ui/node_modules/mocha/lib/runner.js:369:14)
    at /Users/dondi/Documents/Main/Code/git/specialx/material-ui/node_modules/mocha/lib/runner.js:379:7
    at next (/Users/dondi/Documents/Main/Code/git/specialx/material-ui/node_modules/mocha/lib/runner.js:303:14)
    at Immediate._onImmediate (/Users/dondi/Documents/Main/Code/git/specialx/material-ui/node_modules/mocha/lib/runner.js:347:5)
    at processImmediate (timers.js:637:19)

I’ll dig in a bit more to see what’s up. The discussion here and in the PR is giving me a better big-picture sense of what’s going on and so I’m getting some other ideas now.

dondi added a commit to dondi/material-ui that referenced this issue Jul 9, 2019
oliviertassinari pushed a commit to dondi/material-ui that referenced this issue Jul 9, 2019
oliviertassinari pushed a commit to dondi/material-ui that referenced this issue Jul 9, 2019
oliviertassinari pushed a commit to dondi/material-ui that referenced this issue Jul 13, 2019
oliviertassinari pushed a commit that referenced this issue Jul 15, 2019
* #16491 Skip syncHeight in TextareaAutosize if the input ref is null.

* #16491 Revise null check to only have an effect when NODE_ENV === 'test'.

* [test] Add testJsonRenderer step

* Sebastian review
@BobrImperator
Copy link

Just stumbled upon this, my solution for this was to not use react-test-renderer, but render from @testing-library/react

it("renders", () => {
  const { asFragment } = render(<Header text="Hello!" />);
  expect(asFragment()).toMatchSnapshot();
});

Shoutout to https://www.leighhalliday.com/introduction-react-testing-library where I was able to find this API for generating snapshots.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
component: TextareaAutosize The React component. good first issue Great for first contributions. Enable to learn the contribution process. test
Projects
None yet
Development

Successfully merging a pull request may close this issue.

3 participants