I have written a component that does two things :
netInfo.isConnected
change and is false
or when netInfo.isInternetReachable
change and is still false
after 3 seconds
netInfo.isConnected
or netInfo.isInternetReachable
change to true
For (1) hiding after 3 seconds is possible by creating a ref that can be readen 3 seconds later.
(2) is pretty straightforward, a simple useEffect
can help me to do that, my components looks like this:
export function OfflineModeContainer({ children }: { children: ReactNode }) {
const netInfo = useNetInfo()
const [show, setShow] = useState(!netInfo.isConnected)
const isInternetReachable = useRef(netInfo.isInternetReachable)
useEffect(() => {
setShow(!netInfo.isConnected)
}, [netInfo.isConnected])
useEffect(() => {
isInternetReachable.current = netInfo.isInternetReachable
let timer: number | undefined
if (!isInternetReachable.current) {
timer = setTimeout(() => {
if (!isInternetReachable.current) {
setShow(true)
}
}, 3000)
} else {
setShow(false)
}
return () => {
if (timer) {
clearInterval(timer)
}
}
}, [netInfo.isInternetReachable])
return (
<View>
<View>{children}</View>
{show ? (
<View>
<View>{t`aucune connexion internet.`}</<View>
</<View>
) : null}
</View>
)
}
This works fine. I now want to test both useEffect
using React testing library.
setTimeout
asynchronous operation?clearTimer
https://codesandbox.io/s/react-native-test-forked-7nc5zc
Unfortunately, I can't use mock in codesandbox due to known open issue with jest.mock
, still open until 2018 https://github.com/codesandbox/codesandbox-client/issues/513
For this reason, I add the testing logs here:
$ TZ=UTC JEST=true jest --forceExit /home/dka/workspace/github.com/pass-culture/pass-culture-app-native/src/libs/network/__tests__/OfflineModeContainer.test.tsx
FAIL src/libs/network/__tests__/OfflineModeContainer.test.tsx (5.178 s)
<OfflineModeContainer />
✓ should render children and show banner when offline at init when isConnected is false (36 ms)
✓ should render children and not show banner when offline at init when isConnected is true (5 ms)
✕ should not show "aucune connexion internet." at init, then show when isConnected is false, then hide when isConnected switch back to true (558 ms)
● <OfflineModeContainer /> › should not show "aucune connexion internet." at init, then show when isConnected is false, then hide when isConnected switch back to true
expect(received).toBeFalsy()
Received: {"_fiber": {"_debugHookTypes": null, "_debugID": 163, "_debugNeedsRemount": false, "_debugOwner": [FiberNode], "_debugSource": null, "actualDuration": 0, "actualStartTime": -1, "alternate": null, "child": [FiberNode], "childLanes": 0, "dependencies": null, "elementType": [Function Component], "firstEffect": null, "flags": 1, "index": 0, "key": null, "lanes": 0, "lastEffect": null, "memoizedProps": [Object], "memoizedState": null, "mode": 0, "nextEffect": null, "pendingProps": [Object], "ref": null, "return": [FiberNode], "selfBaseDuration": 0, "sibling": null, "stateNode": [Component], "tag": 1, "treeBaseDuration": 0, "type": [Function Component], "updateQueue": [Object]}}
50 | </OfflineModeContainer>
51 | )
> 52 | expect(await renderAPI.queryByText('aucune connexion internet.')).toBeFalsy()
| ^
53 | })
54 | })
55 |
This is how I solved this problem:
import React from 'react'
import { View, Text } from 'react-native'
import { useNetInfoContext as useNetInfoContextDefault } from 'libs/network/NetInfoWrapper'
import { OfflineModeContainer } from 'libs/network/OfflineModeContainer'
import { render } from '@testing-library/react-native'
jest.mock('libs/network/useNetInfo', () => jest.requireMock('@react-native-community/netinfo'))
const mockUseNetInfoContext = useNetInfoContextDefault as jest.Mock
describe('<OfflineModeContainer />', () => {
mockUseNetInfoContext.mockImplementation(() => ({ isConnected: false }))
it('should not show "aucune connexion internet." at init, then show when isConnected is false, then hide when isConnected switch back to true', () => {
mockUseNetInfoContext.mockImplementationOnce(() => ({
isConnected: true,
isInternetReachable: true,
}))
const renderAPI = renderOfflineModeContainer()
expect(renderAPI.queryByText('aucune connexion internet.')).toBeFalsy()
mockUseNetInfoContext.mockImplementationOnce(() => ({
isConnected: false,
isInternetReachable: false,
}))
renderAPI.rerender(getJsx())
expect(renderAPI.queryByText('aucune connexion internet.')).toBeTruthy()
})
})
// for rerender, it cannot be static, it has to be new
const getJsx = () => (
<OfflineModeContainer>
<View>
<Text>Hello World</Text>
</View>
</OfflineModeContainer>
)
function renderOfflineModeContainer() {
return render(getJsx())
}