enzyme
enzyme copied to clipboard
The state is not reflect in the view when using useEffect and setTimeout
Current behavior
SomeComponent.tsx
:
import React, { ReactElement, useEffect, useState } from 'react';
import { StyledNotifyButton } from './styles';
export const SomeComponent = (): ReactElement => {
const [showNotifyButton, toggleNotifyButton] = useState(false);
useEffect(() => {
setTimeout(() => {
toggleNotifyButton(true);
}, 5000);
}, [toggleNotifyButton]);
console.log('showNotifyButton: ', showNotifyButton);
return (
<div>
<StyledNotifyButton visible={showNotifyButton} />
</div>
);
};
SomeComponent.test.tsx
:
import { mount, ReactWrapper } from 'enzyme';
import React from 'react';
import { act } from 'react-dom/test-utils';
import { SomeComponent } from './SomeComponent';
describe('67440874', () => {
let wrapper: ReactWrapper;
beforeAll(() => {
jest.useFakeTimers();
wrapper = mount(<SomeComponent />);
});
it('should pass', () => {
let notifyButton = wrapper.find('StyledNotifyButton');
expect(notifyButton.prop('visible')).toBe(false);
act(() => {
jest.runOnlyPendingTimers();
});
// wrapper.update();
expect(wrapper.find('StyledNotifyButton').prop('visible')).toBeTruthy();
});
});
If I don't call wrapper.update()
method, the changed state will not reflect in the view.
test result
FAIL examples/67440874/SomeComponent.test.tsx
67440874
✕ should pass (11 ms)
● 67440874 › should pass
expect(received).toBeTruthy()
Received: false
18 | });
19 | // wrapper.update();
> 20 | expect(wrapper.find('StyledNotifyButton').prop('visible')).toBeTruthy();
| ^
21 | });
22 | });
23 |
at Object.<anonymous> (examples/67440874/SomeComponent.test.tsx:20:64)
console.log
showNotifyButton: false
at SomeComponent (examples/67440874/SomeComponent.tsx:13:11)
console.log
showNotifyButton: true
at SomeComponent (examples/67440874/SomeComponent.tsx:13:11)
Test Suites: 1 failed, 1 total
Tests: 1 failed, 1 total
Snapshots: 0 total
Time: 1.542 s, estimated 2 s
Expected behavior
I don't know why I should call wrapper.update()
here. As you can see, the state is changed, but the value of the visible
prop is not changed.
API
- [ ] shallow
- [x] mount
- [ ] render
Version
library | version |
---|---|
enzyme | ^3.11.0 |
react | ^16.14.0 |
react-dom | ^16.14.0 |
react-test-renderer | |
adapter (below) |
Adapter
- [x] enzyme-adapter-react-16
- [ ] enzyme-adapter-react-16.3
- [ ] enzyme-adapter-react-16.2
- [ ] enzyme-adapter-react-16.1
- [ ] enzyme-adapter-react-15
- [ ] enzyme-adapter-react-15.4
- [ ] enzyme-adapter-react-14
- [ ] enzyme-adapter-react-13
- [ ] enzyme-adapter-react-helper
- [ ] others ( )
I don't know why I should call wrapper.update() here.
Enzyme effectively takes a snapshot of your UI after the initial mount
and when you call various Enzyme methods (eg. wrapper.setProps(...)
). If you do something which causes the UI outside of Enzyme, such as the act
block here, you need to tell Enzyme that the output as changed and it should take a new snapshot that you can then query. This is what wrapper.update()
does.
From experience working with developers on my team this aspect of Enzyme can be pretty confusing if you've not developed a good mental model of how it works. It would definitely improve the user experience if the need for this could be eliminated.
@robertknight fwiw v2 did this automatically; unfortunately (as i've come to believe in time) v3 changed this behavior to require an explicit update
.
I have same issue here but in my case, I dont use any setTimeout
magic to make Enzyme snapshot out of date.
https://github.com/enzymejs/enzyme/issues/2542
Seem that Enzyme's .setProps()
does not make Enzyme sync the snapshot as expected. It still requires an additional .update()
to be call.