Using jest and useFormik, getting an error on destructuring - reactjs

I have a component that is following the useFormik pattern like this.
const formikOptions:FormikConfig<FormType> = someFormikOptions
const formik = useFormik(formikOptions)
const {values, handleSubmit, dirty, } = formik
Then I toss in a <FormikProvider value={formik}> in the component, and everything works fine on the front end when I run everything. No errors reported at build time, no console warnings, no issues. Woohoo!
Then I come to my spec file where we use jest and enzyme. We recently added an option that resets all mocks between tests (not my decision, not a decision that I can overrule, please do not tell me to yell at my CTO to fix this). One of my now-failing tests looks like this:
decribe('the component name',()=>{
it('should render',()=>{
const wrapper = shallow(<TheComponent {...ownProps} />)
expect(wrapper).toMatchSnapshot()
}
}
When I run this code, I'm getting an error:
TypeError: Cannot destructure property 'values' of 'formik' as it is undefined.
I've tried spying on useFormik and mocking a return value with a clearly defined values return object, but I've been unsuccesful. I'm not sure what I'm doing wrong here. Whenever I google, I'm getting information strictly for useFormikContext, never for useFormik. Why am I seeing this type error, and how can I mend it?

Related

TypeScript + React | How to type check all Component props

How can I make sure that TS is type checking all available properties of a component? The moment one property is incorrect, the rest of the "upcoming" props are not checked anymore - at least I don't see an error shown in my IDE (Webstorm).
Also see code example: TS Playground
I encountered several "issues" or risky moments where one is using #ts-ignore and/or #ts-expect-error. It leads to false positives as it doesn't show you any error anymore although you only ignored certain ones, but indeed not all of them.
I reported a TS bug about this but it is actually intended behavior, that e.g. #ts-expect-error is running after all the type-checking. (see: https://github.com/microsoft/TypeScript/issues/49972)
Here the code you can try it out on your own:
import { React, FunctionComponent } from 'react'
export const MyComponent: FunctionComponent<{
validProp: string
}> = (props) => {
return <>MyComponent</>
}
export const MyComponent2: FunctionComponent<{}> = () => {
return (
<>
<MyComponent
validProp='validProp'
invalidProp1
invalidProp2
/>
<MyComponent
validProp='validProp'
// #ts-expect-error
invalidProp1
invalidProp2 // <-- here it should still throw and show me that this prop is invalid
/>
<MyComponent
validProp='validProp'
invalidProp1
// #ts-expect-error
invalidProp2
/>
</>
)
}
But, I still see issues with this. How can I make sure that TS is complaining about other wrong props although the previous incorrect prop has been ignored?
One "solution" might be only using #ts-expect-error at the end of all component prop definition, but how to ensure it is always ok and no-one is changing the order unintentionally so that other incorrect props are potentially ignored in the future?
I didn't find something according to my question. The only topic I found is this. But also here, the problems are still present, so I can actually not really agree with those solutions provided there.
How can I declare/ignore a missing JSX type?
Where can I improve?
Is this a limitation that I can't work around?

Getting Uncaught TypeError: onKeyPress is not a function in React typescript functional component

I am getting the following error:
Uncaught TypeError: onKeyPress is not a function in React typescript functional component in console when ever I am giving input in textbox.
Unable to know what should be done fix this issue. My requirement is based on some condition I need to call someFunctionToDoSomeStuff on onKeyPress event otherwise it should behave normally. Though I am able to track event onKeyPress when using the same component from another component but I want to fix this issue. So please help.
interface TextBxIProps = {
onKeyPress?: (e: React.KeyBoardEvent<HTMLInputElement>)=> void
okFlag?:boolean
}
const MyTextBox<TextBxIProps> = ({onKeyPress, okFlag=true})=> {
const someFunctionToDoSomeStuff = (e: React.KeyBoardEvent<HTMLInputElement>)=>{
// this is working fine
}
return(
<TextBoxComponent onKeyPress={(event)=> {
if(okFlag){
return someFunctionToDoSomeStuff(event)
}
else{
return onKeyPress(event) // this giving issue I think but unable to figure how shoudl I fix it as I want to return normal event onKeyPress on some condition
}
})}/>
onKeyPress is indeed not defined anywhere, so the error message is correct.
If "it should behave normally" means that you want native behaviour, there is nothing you need to do, i.e. you don't need to return anything (no need for an else). That's the default behaviour.
Only if you want to prevent the "normal behaviour" you need to explicitly instruct:
event.preventDefault()

How to test react components props (expect component to be called with)

I need to test that a react component is called with opened={true} prop after an button click is fired. I am using testing-library ( testing-library/react + testing-library/jest-dom).
I mocked the Component using something like
import Component from "./path-to-file/component-name"
...
jest.mock("./path-to-file/component-name", () => {
return jest.fn().mockImplementation(() => {
return null
})
})
I first tried with:
expect(Component).toBeCalledWith(expect.objectContaining({"opened": true}))
expect(Component).toHaveBeenCalledWith(expect.objectContaining({"opened": true}))
expect(Component).toHaveBeenLastCalledWith(expect.objectContaining({"opened": true}))
but I got Error: expect(jest.fn()).toBeCalledWith(...expected).
Same went for expect.objectContaining({"opened": expect.anything()})
And even for expect(Component).toBeCalledWith(expect.anything())
And the difference is empty array:
I also tried with expect(ChartMenu.mock).toBeCalledWith(expect.anything()). I got a different error but still not working (this time the error was Error: expect(received).toBeCalledWith(...expected) + Matcher error: received value must be a mock or spy function)
Thank you in advice!
EDIT: here is a simplified version of the component I want to test:
const Component = () => {
const [chartMenuOpened, setChartMenuOpened] = useState(false)
return (
<Flex>
<EllipseIcon onClick={() => setChartMenuOpened(true)}>
+
</EllipseIcon>
<ChartMenu
opened={chartMenuOpened}
close={() => setChartMenuOpened(false)}
/>
</Flex>
)
}
Basically I want to make sure that when the + icon is clicked the menu will be opened (or called with open value). The issue is that I cannot render ChartMenu because it needs multiple props and redux state.
I was able in the end to mock useState in order to check that the setState was properly called from the icon component (in order to make sure there won't be future changes on the component that will break this using this post).
But I would still really appreciate an answer to the question: if there is any way to create a spy or something similar on a react component and check the props it was called with? Mostly because this was a rather simple example and I only have one state. But this might not always be the case. Or any good idea on how to properly test this kind if interaction would be really appeciated.
I think you are on the right track to test if the component has been called with that prop, it's probably the syntax issue in your code
I learn this trick from colleague and you can try to see if this helps fix your issue.
expect(Component).toHaveBeenCalledWith(
expect.objectContaining({
opened: true,
}),
expect.anything()
);
While the question on how to is answered, I did some extra research on this and it seems like in React components there is a second parameter refOrContext (so basically most of the time it's an empty object, but it can also be a ref or a context)
Despite pointing out the reason for the behavior, I also wanted to highlight that it is safer to use expect.anything() as the second argument (rather than just {} which would work only in most of the cases ):
More information about React second argument here

Errors when using React Testing Library and renderHook to test hooks with multiple contexts

Solved
Issue is tracked on github
I was attempting to test custom hooks using the react testing library, and the hook I was trying to put under test requires multiple context providers to work correctly. (authentication and notification)
The documentation here only outlines creating a wrapper with a single provider like so:
const wrapper = ({ children }) => <ContextProvider>{children}</ContextProvider>
However, my implementation needed something more complex, like so:
const wrapper = ({ children }) => (
<ToastProvider>
<NotificationProvider>
<AuthProvider>{children}</AuthProvider>
</NotificationProvider>
</ToastProvider>
);
This was was failing at every attempt with the errors:
TypeError: parentInstance.children.indexOf is not a function
OR
Invariant Violation: Drop(...): Nothing was returned from render. This usually means a return statement is missing. Or, to render nothing, return null.
Leading me to believe there was no clear way to provide the right context without abandoning renderHook all together and building a test component that could fire off the necessary behaviors manually.
After a lot more digging I found this error buried in the logs:
Warning: An invalid container has been provided. This may indicate that another renderer is being used in addition to the test renderer. (For example, ReactDOM.createPortal inside of a ReactTestRenderer tree.) This is not supported.
Surely enough, it turns out there is a conflict with react-test-renderer and react-dom which causes calls to ReactDOM.createPortal to fail when under test. I assume this is somewhere in the ToastProvider, but the workaround is pretty simple.
Solved by adding this to the top of my test:
ReactDOM.createPortal = node => node

Getting console errors when using jest.mock for React components

When I mock a react component like this jest.mock('react-slick', () => 'div'); I get console errors for unknown props passed to tag. I know these errors don't mean anything in this case, but it's quite annoying to see them in the output:
Is there a way to disable these errors? Maybe just don't pass any props to the component when it is mocked?
You can just mock the console.error, like so:
console.error = jest.genMockFn()
Jest will still fail the tests regardless of the above.

Resources