Skip to content

Commit b1decd7

Browse files
authored
Merge pull request #707 from Iterable/feature/MOB-12159-add-tests-for-useappstatelistener
[MOB-12159] add-tests-for-useappstatelistener
2 parents 763709c + 9249cea commit b1decd7

File tree

2 files changed

+184
-0
lines changed

2 files changed

+184
-0
lines changed
Lines changed: 184 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,184 @@
1+
import { renderHook, act } from '@testing-library/react-native';
2+
import { AppState } from 'react-native';
3+
4+
import { useAppStateListener } from './useAppStateListener';
5+
6+
describe('useAppStateListener', () => {
7+
let mockListener: { remove: jest.Mock };
8+
let addEventListenerSpy: jest.SpyInstance;
9+
let currentStateGetter: jest.SpyInstance;
10+
11+
beforeEach(() => {
12+
jest.clearAllMocks();
13+
mockListener = { remove: jest.fn() };
14+
15+
// Spy on AppState methods
16+
addEventListenerSpy = jest.spyOn(AppState, 'addEventListener').mockReturnValue(mockListener);
17+
18+
// Mock the currentState property
19+
Object.defineProperty(AppState, 'currentState', {
20+
get: jest.fn(() => 'active'),
21+
configurable: true,
22+
});
23+
currentStateGetter = jest.spyOn(AppState, 'currentState', 'get');
24+
});
25+
26+
afterEach(() => {
27+
addEventListenerSpy.mockRestore();
28+
currentStateGetter.mockRestore();
29+
});
30+
31+
it('should return initial app state', () => {
32+
// GIVEN the hook is rendered
33+
const { result } = renderHook(() => useAppStateListener());
34+
35+
// THEN it should return the current app state
36+
expect(result.current).toBe('active');
37+
});
38+
39+
it('should set up AppState listener on mount', () => {
40+
// WHEN the hook is rendered
41+
renderHook(() => useAppStateListener());
42+
43+
// THEN AppState.addEventListener should be called with correct parameters
44+
expect(addEventListenerSpy).toHaveBeenCalledWith(
45+
'change',
46+
expect.any(Function)
47+
);
48+
});
49+
50+
it('should update state when app state changes to background', () => {
51+
// GIVEN the hook is rendered
52+
const { result } = renderHook(() => useAppStateListener());
53+
54+
// Get the listener callback
55+
const listenerCallback = addEventListenerSpy.mock.calls[0][1];
56+
57+
// WHEN app state changes to background
58+
act(() => {
59+
listenerCallback('background');
60+
});
61+
62+
// THEN the hook should return the new state
63+
expect(result.current).toBe('background');
64+
});
65+
66+
it('should update state when app state changes to inactive', () => {
67+
// GIVEN the hook is rendered
68+
const { result } = renderHook(() => useAppStateListener());
69+
70+
// Get the listener callback
71+
const listenerCallback = addEventListenerSpy.mock.calls[0][1];
72+
73+
// WHEN app state changes to inactive
74+
act(() => {
75+
listenerCallback('inactive');
76+
});
77+
78+
// THEN the hook should return the new state
79+
expect(result.current).toBe('inactive');
80+
});
81+
82+
it('should update state when app state changes back to active', () => {
83+
// GIVEN the hook is rendered and state is initially background
84+
const { result } = renderHook(() => useAppStateListener());
85+
const listenerCallback = addEventListenerSpy.mock.calls[0][1];
86+
87+
act(() => {
88+
listenerCallback('background');
89+
});
90+
expect(result.current).toBe('background');
91+
92+
// WHEN app state changes back to active
93+
act(() => {
94+
listenerCallback('active');
95+
});
96+
97+
// THEN the hook should return active
98+
expect(result.current).toBe('active');
99+
});
100+
101+
it('should handle multiple state changes', () => {
102+
// GIVEN the hook is rendered
103+
const { result } = renderHook(() => useAppStateListener());
104+
const listenerCallback = addEventListenerSpy.mock.calls[0][1];
105+
106+
// WHEN multiple state changes occur
107+
act(() => {
108+
listenerCallback('background');
109+
});
110+
expect(result.current).toBe('background');
111+
112+
act(() => {
113+
listenerCallback('inactive');
114+
});
115+
expect(result.current).toBe('inactive');
116+
117+
act(() => {
118+
listenerCallback('active');
119+
});
120+
expect(result.current).toBe('active');
121+
});
122+
123+
it('should clean up listener on unmount', () => {
124+
// GIVEN the hook is rendered
125+
const { unmount } = renderHook(() => useAppStateListener());
126+
127+
// WHEN the component unmounts
128+
unmount();
129+
130+
// THEN the listener should be removed
131+
expect(mockListener.remove).toHaveBeenCalledTimes(1);
132+
});
133+
134+
it('should handle initial state with different AppState.currentState', () => {
135+
// GIVEN AppState.currentState is set to background
136+
currentStateGetter.mockReturnValue('background');
137+
138+
// WHEN the hook is rendered
139+
const { result } = renderHook(() => useAppStateListener());
140+
141+
// THEN it should return the background state
142+
expect(result.current).toBe('background');
143+
});
144+
145+
it('should handle initial state with inactive AppState.currentState', () => {
146+
// GIVEN AppState.currentState is set to inactive
147+
currentStateGetter.mockReturnValue('inactive');
148+
149+
// WHEN the hook is rendered
150+
const { result } = renderHook(() => useAppStateListener());
151+
152+
// THEN it should return the inactive state
153+
expect(result.current).toBe('inactive');
154+
});
155+
156+
it('should not call addEventListener multiple times on re-renders', () => {
157+
// GIVEN the hook is rendered
158+
const { rerender } = renderHook(() => useAppStateListener());
159+
160+
// WHEN the component re-renders
161+
rerender(() => useAppStateListener());
162+
rerender(() => useAppStateListener());
163+
164+
// THEN addEventListener should only be called once
165+
expect(addEventListenerSpy).toHaveBeenCalledTimes(1);
166+
});
167+
168+
it('should maintain state consistency across re-renders', () => {
169+
// GIVEN the hook is rendered and state changes
170+
const { result, rerender } = renderHook(() => useAppStateListener());
171+
const listenerCallback = addEventListenerSpy.mock.calls[0][1];
172+
173+
act(() => {
174+
listenerCallback('background');
175+
});
176+
expect(result.current).toBe('background');
177+
178+
// WHEN the component re-renders
179+
rerender(() => useAppStateListener());
180+
181+
// THEN the state should remain consistent
182+
expect(result.current).toBe('background');
183+
});
184+
});

src/hooks/index.ts

Whitespace-only changes.

0 commit comments

Comments
 (0)