Given the following simple components:
function ValueInput(props) {
const [val, setVal] = useState(props.value);
function onChange (val) {
setVal(val);
props.onValueChanged(val);
}
return <input value={val} onChange={onChange}/>;
}
function MyComponent(props) {
const [val, setVal] = useState(props.value);
function onValueChanged (val) {
setVal(val);
}
return (
<div>
<div>{val}</div>
<ValueInput value={val} onValueChanged={onValueChanged}/>
</div>
);
}
I'm mounting them in order to test them with Enzyme and Jest:
const component = mount(<MyComponent value={42}/>);
const inputEl = component.find('input');
How do change the value of the inner component in order to that any change to ValueInput is reflected on MyComponent? I'm trying with the following code, but it doesn't work:
console.log(component.debug());
valueInputEl.setProps({value: 24});
// component.update();
console.log(component.debug());
And I get the following error:
ReactWrapper::setProps() can only be called on the root
You could shallow mount your MyComponent and then test it by triggering the onValueChanged prop of your ValueInput child component and test the changes in your state by checking the value prop of the child component.
test('That the parent element is changed when the child component is changed', () => {
const component = shallow(<MyComponent value={42} />);
component.find(ValueInput).prop('onValueChanged')(24);
expect(component.find(ValueInput).prop('value')).toBe(24);
console.log(component.debug());
});
And test for the behaviour of the ValueInput's onChange methods in its own component tests so that it acts more like a unit test.
Related
I have a function that should get invoked when a prop (newItems) changes:
componentDidUpdate(prevProps) {
const { title } = this.state;
const { newItems } = this.props;
const { newItems: prevNewItems } = prevProps;
if (prevNewItems !== newItems) {
this.updateTitle(title, newItems); // testing if this method gets called
}
}
The problem on the test below is that componentDidUpdate lifecycle hook doesn't register a new prop after I call setProps, thus the spy receives "0 number of calls"
it('calls updateTitle when newItems changes', () => {
const wrapper = mount(
<Provider store={store}>
<MyComponent {...props} newItems={0}/>
</Provider>,
);
const MyComponentWrapper = wrapper.find('MyComponent');
const spy = jest.spyOn(MyComponentWrapper.instance(), 'updateTitle');
wrapper.setProps({
children: <MyComponent {...props} newItems={1} />
});
wrapper.update();
expect(spy).toHaveBeenCalled(); // Received number of calls: 0 (should be 1)
});
How can I update props on a child component (MyComponent) that is wrapped in a Provider?
You can update the props of the child component (MyComponent) by passing new props to the wrapper.setProps() method. Instead of passing children property, pass the updated newItems property:
wrapper.setProps({
newItems: 1
});
You can then call wrapper.update() to re-render the component with the updated props.
After this, the componentDidUpdate lifecycle method should be triggered, and the updateTitle method should be called with the updated newItems prop.
I'm trying to test a Parent component which has a method that has been called from Child component. An example:
Parent.js
export default function Parent() {
const methodToTest = () => {
//method code
}
return (
<div>
<Child onChange={methodToTest} />
</div>
)
}
Child.js
export default function Child({ onChange }) {
const handleChange = () => {
onChange()
}
//InaccesibleElement is a component from a library that not render the element in test instance.
return (
<div>
....
<InaccesibleElement onClick={() => handleChange()} />
</div>
)
}
I would thinking to mock Child and inject into Parent component but doesn't work or I don't know how to do it correctly. My test example:
Parent.test.js
function ChildMock({ onChange }) {
const handleChange = () => {
onChange("mockedValue");
}
return(
<button onClick={() => handleChange()}>click me</button>
)
}
jest.mock('../components/Child', () => {
return { Child: ChildMock}
})
test("test parent method", () => {
const component = render(<Parent />);
const button = component.getByText("click me");
//rest of the test...
})
jest.mock() throws an exception:
"Reference error: The module factory of jest.mock() is not allowed to reference any out-of-scope variables.
Invalid variable access: BarGraphContainerMock
Allowed objects: Array, ArrayBuffer, Atomics, BigInt, BigInt64Array, BigUint64Array, Boolean, Buffer, DataView, Date, ....
Note: This is a precaution to guard against uninitialized mock variables. If it is ensured that the mock is required lazily, variable names prefixed with mock (case insensitive) are permitted."
Thanks.
I am getting error on using toHaveBeenCalled, Please correct me where am going wrong
code:
jsx
<item
onClick={ load ? undefined : onClick}
>
test
test('render', () => {
const MockItems = jest.fn()
const prop = {
onClick: MockItems,
}
const onclickProp= output.find(item).props().onClick
onclickProp(undefined)
expect(props.onClick).toHaveBeenCalled()//error
}
error
expect(props.onClick).toHaveBeenCalled()
Warning: An update to null inside a test was not wrapped in act(...).
When testing, code that causes React state updates should be wrapped into act(...):
act(() => {
/* fire events that update state */
});
/* assert on the output */
If the item is inside any other component as a child component you need to dive() on the wrapper component. Shallow rendering gives you access to only outer/parent component layout.
describe('item parent Component', () => {
let wrapper,instance
beforeEach(() => {
mockProps = {
handleClick: jest.fn()
}
const component = (<parent {...mockProps} />)
wrapper = shallow(component).dive()
})
it('item is clicked', () => {
wrapper.find(item).simulate('click')
expect(handleClick).toHaveBeenCalled()
})
)}
<item onClick={ load ? undefined : onClick} >
I'm using jest/enzyme and want to check existence child elements of React component
if i have function as component child
const children = () => (
<>
<div>...</div>
<div>...</div>
</>
)
return <Component>{children}</Component>;
why i can't do like this
test('Should render div', () => {
wrapper = shallow(<MyComponent />);
const component = wrapper.find(Component);
expect(component.exists()).toBe(true); //return true
const children = wrapper.find('div')
expect(children.exists()).toBe(false); //return false
});
Your children function is basically a render prop and shallow doesn't render it. You can however trigger the rendering by calling it as a function like
shallow(
shallow(<MyComponent />)
.find(Component)
.prop('children')()
)
So your test will look like
test('Should render div', () => {
wrapper = shallow(<MyComponent />);
const component = wrapper.find(Component);
expect(component.exists()).toBe(true); //return true
const renderProp = shallow(component.prop('children')());
const children = renderProp.find('div');
expect(children.exists()).toBe(true);
});
How do I test input.focus() in enzyme. I am writing the script with react. My code is below:
public inputBox: any;
componentDidUpdate = () => {
setTimeout(() => {
this.inputBox.focus();
}, 200);
}
render() {
return (
<div>
<input
type = 'number'
ref = {element => this.inputBox = element } />
</div>
);
}
You can use mount instead of shallow.
Then you can compare document.activeElement and the input DOM node for equality.
const output = mount(<MyFocusingComponent/>);
assert(output.find('input').node === document.activeElement);
See https://github.com/airbnb/enzyme/issues/316 for more details.
Per React 16.3 updates... using createRef for anyone visiting this post today, if you rearrange the original component to use the new ref api
class InputBox extends PureComponent {
constructor(props) {
super(props);
this.inputRef = React.createRef();
}
componentDidMount() {
this.inputRef.current.focus();
}
render() {
return (
<input
ref={this.inputRef}
/>
);
}
}
Then in your test spec
it("Gives immediate focus on to name field on load", () => {
const wrapper = mount(<InputBox />);
const { inputRef } = wrapper.instance();
jest.spyOn(inputRef.current, "focus");
wrapper.instance().componentDidMount();
expect(inputRef.current.focus).toHaveBeenCalledTimes(1);
});
Notice the use of the inputRef.current attribute which references the currently assigned DOM node.
Other approach is to test if element gains focus, i.e. focus() is called on node element. To achieve this, focused element need to be referenced via ref tag like it takes place in your example – reference was assigned to this.inputBox. Consider example below:
const wrapper = mount(<FocusingInput />);
const element = wrapper.instance().inputBox; // This is your input ref
spyOn(element, 'focus');
wrapper.simulate('mouseEnter', eventStub());
setTimeout(() => expect(element.focus).toHaveBeenCalled(), 250);
This example uses Jasmine's spyOn, though you can use any spy you like.
I just had the same issue and solved using the following approach:
My setup is Jest (react-create-app) + Enzyme:
it('should set the focus after render', () => {
// If you don't create this element you can not access the
// document.activeElement or simply returns <body/>
document.body.innerHTML = '<div></div>'
// You have to tell Enzyme to attach the component to this
// newly created element
wrapper = mount(<MyTextFieldComponent />, {
attachTo: document.getElementsByName('div')[0]
})
// In my case was easy to compare using id
// than using the whole element
expect(wrapper.find('input').props().id).toEqual(
document.activeElement.id
)
})
This worked for me when using mount and useRef hook:
expect(wrapper.find('input').get(0).ref.current).toEqual(document.activeElement)
Focus on the particular element can be checked using selectors.
const wrapper = mount(<MyComponent />);
const input = wrapper.find('input');
expect(input.is(':focus')).toBe(true);
Selecting by data-test attribute or something similar was the most straight forward solution I could come up with.
import React, { Component } from 'react'
import { mount } from 'enzyme'
class MyComponent extends Component {
componentDidMount() {
if (this.inputRef) {
this.inputRef.focus()
}
}
render() {
return (
<input data-test="my-data-test" ref={input => { this.inputRef = input } } />
)
}
}
it('should set focus on mount', () => {
mount(<MyComponent />)
expect(document.activeElement.dataset.test).toBe('my-data-test')
})
This should work
const wrapper = mount(<MyComponent />);
const input = wrapper.find('input');
expect(input).toHaveFocus();