Skip to content

Commit 5c137da

Browse files
committed
chore: reduce mocking to minimum, prefer native impl
1 parent fc3c7cb commit 5c137da

File tree

1 file changed

+174
-75
lines changed

1 file changed

+174
-75
lines changed
Lines changed: 174 additions & 75 deletions
Original file line numberDiff line numberDiff line change
@@ -1,111 +1,210 @@
1-
jest.mock('react-native-teleport', () => {
2-
const React = require('react');
3-
const { Text, View } = require('react-native');
4-
5-
return {
6-
Portal: ({
7-
children,
8-
hostName,
9-
name,
10-
}: {
11-
children: React.ReactNode;
12-
hostName?: string;
13-
name: string;
14-
}) => (
15-
<View testID={`portal-${name}`}>
16-
<Text testID={`portal-${name}-host`}>{hostName ?? 'none'}</Text>
17-
{children}
18-
</View>
19-
),
20-
PortalHost: ({ name }: { name: string }) => <Text testID={`portal-host-${name}`}>{name}</Text>,
21-
PortalProvider: View,
22-
usePortal: jest.fn().mockReturnValue({ removePortal: jest.fn() }),
23-
};
24-
});
25-
26-
jest.mock('../../../state-store/message-overlay-store', () => ({
27-
clearClosingPortalLayout: jest.fn(),
28-
createClosingPortalLayoutRegistrationId: jest.fn(),
29-
setClosingPortalLayout: jest.fn(),
30-
useShouldTeleportToClosingPortal: jest.fn(),
31-
}));
32-
331
jest.mock('../../../state-store', () => {
34-
const mockedMessageOverlayStore = jest.requireMock('../../../state-store/message-overlay-store');
2+
const actual = jest.requireActual('../../../state-store');
3+
const createClosingPortalLayoutRegistrationId = jest.fn(() => 'registration-1');
4+
5+
return new Proxy(actual, {
6+
get(target, prop, receiver) {
7+
if (prop === 'createClosingPortalLayoutRegistrationId') {
8+
return createClosingPortalLayoutRegistrationId;
9+
}
3510

36-
return {
37-
...mockedMessageOverlayStore,
38-
};
11+
return Reflect.get(target, prop, receiver);
12+
},
13+
});
3914
});
4015

4116
import React from 'react';
4217
import { Text } from 'react-native';
4318

4419
import { act, cleanup, render, screen } from '@testing-library/react-native';
4520

46-
import * as stateStore from '../../../state-store/message-overlay-store';
21+
import * as stateStore from '../../../state-store';
4722
import { PortalWhileClosingView } from '../PortalWhileClosingView';
4823

49-
const mockedCreateClosingPortalLayoutRegistrationId =
50-
stateStore.createClosingPortalLayoutRegistrationId as jest.Mock;
51-
const mockedClearClosingPortalLayout = stateStore.clearClosingPortalLayout as jest.Mock;
52-
const mockedUseShouldTeleportToClosingPortal =
53-
stateStore.useShouldTeleportToClosingPortal as jest.Mock;
24+
const BASE_RECT = { h: 48, w: 120, x: 12, y: 24 };
25+
26+
const flushAnimationFrameQueue = () => {
27+
act(() => {
28+
jest.runAllTimers();
29+
});
30+
};
31+
32+
const TeleportStateProbe = ({
33+
hostName,
34+
id,
35+
testID,
36+
}: {
37+
hostName: string;
38+
id: string;
39+
testID: string;
40+
}) => {
41+
const shouldTeleport = stateStore.useShouldTeleportToClosingPortal(hostName, id);
42+
43+
return <Text testID={testID}>{shouldTeleport ? 'true' : 'false'}</Text>;
44+
};
45+
46+
const BlacklistRegistrar = ({ hostNames }: { hostNames: string[] }) => {
47+
stateStore.useClosingPortalHostBlacklist(hostNames);
48+
return null;
49+
};
50+
51+
const findPortalNode = (
52+
node: ReturnType<ReturnType<typeof render>['toJSON']>,
53+
portalName: string,
54+
): null | { props?: { hostName?: string; name?: string } } => {
55+
if (!node) {
56+
return null;
57+
}
58+
59+
if (Array.isArray(node)) {
60+
for (const child of node) {
61+
const found = findPortalNode(child, portalName);
62+
if (found) {
63+
return found;
64+
}
65+
}
66+
67+
return null;
68+
}
69+
70+
if (node.props?.name === portalName) {
71+
return node;
72+
}
73+
74+
for (const child of node.children ?? []) {
75+
if (typeof child === 'string') {
76+
continue;
77+
}
78+
79+
const found = findPortalNode(child, portalName);
80+
if (found) {
81+
return found;
82+
}
83+
}
84+
85+
return null;
86+
};
5487

5588
describe('PortalWhileClosingView', () => {
5689
beforeEach(() => {
57-
mockedClearClosingPortalLayout.mockClear();
58-
mockedUseShouldTeleportToClosingPortal.mockReset();
59-
mockedUseShouldTeleportToClosingPortal.mockReturnValue(false);
60-
mockedCreateClosingPortalLayoutRegistrationId.mockReset();
61-
mockedCreateClosingPortalLayoutRegistrationId
62-
.mockReturnValueOnce('registration-1')
63-
.mockReturnValueOnce('registration-2')
64-
.mockReturnValue('registration-fallback');
90+
jest.useFakeTimers();
91+
92+
act(() => {
93+
stateStore.finalizeCloseOverlay();
94+
stateStore.overlayStore.next({
95+
closing: false,
96+
closingPortalHostBlacklist: [],
97+
id: undefined,
98+
});
99+
});
65100
});
66101

67102
afterEach(() => {
68103
cleanup();
104+
105+
act(() => {
106+
stateStore.clearClosingPortalLayout('overlay-composer', 'registration-1');
107+
stateStore.finalizeCloseOverlay();
108+
stateStore.overlayStore.next({
109+
closing: false,
110+
closingPortalHostBlacklist: [],
111+
id: undefined,
112+
});
113+
});
114+
115+
jest.clearAllMocks();
116+
jest.clearAllTimers();
117+
jest.useRealTimers();
69118
});
70119

71-
it('keeps content local when the teleport hook returns false', () => {
72-
render(
73-
<PortalWhileClosingView portalHostName='overlay-composer' portalName='local-portal'>
74-
<Text>Local</Text>
75-
</PortalWhileClosingView>,
120+
it('uses the real store to teleport once the overlay is closing and this host is active', () => {
121+
const { toJSON } = render(
122+
<>
123+
<PortalWhileClosingView portalHostName='overlay-composer' portalName='composer-portal'>
124+
<Text>Composer</Text>
125+
</PortalWhileClosingView>
126+
<TeleportStateProbe
127+
hostName='overlay-composer'
128+
id='registration-1'
129+
testID='teleport-state'
130+
/>
131+
</>,
76132
);
77133

78-
expect(screen.getByTestId('portal-local-portal-host')).toHaveTextContent('none');
79-
});
134+
act(() => {
135+
stateStore.setClosingPortalLayout('overlay-composer', 'registration-1', BASE_RECT);
136+
});
80137

81-
it('teleports content to the closing host when the teleport hook returns true', () => {
82-
mockedUseShouldTeleportToClosingPortal.mockReturnValue(true);
138+
expect(screen.getByTestId('teleport-state')).toHaveTextContent('false');
139+
expect(findPortalNode(toJSON(), 'composer-portal')?.props?.hostName).toBeUndefined();
83140

84-
render(
85-
<PortalWhileClosingView portalHostName='overlay-composer' portalName='teleported-portal'>
86-
<Text>Teleported</Text>
87-
</PortalWhileClosingView>,
88-
);
141+
act(() => {
142+
stateStore.openOverlay('message-1');
143+
stateStore.closeOverlay();
144+
});
145+
flushAnimationFrameQueue();
146+
147+
expect(screen.getByTestId('teleport-state')).toHaveTextContent('true');
148+
expect(findPortalNode(toJSON(), 'composer-portal')?.props?.hostName).toBe('overlay-composer');
149+
});
89150

90-
expect(screen.getByTestId('portal-teleported-portal-host')).toHaveTextContent(
91-
'overlay-composer',
151+
it('keeps the portal local when the host is blacklisted', () => {
152+
const { toJSON } = render(
153+
<>
154+
<BlacklistRegistrar hostNames={['overlay-composer']} />
155+
<PortalWhileClosingView portalHostName='overlay-composer' portalName='composer-portal'>
156+
<Text>Composer</Text>
157+
</PortalWhileClosingView>
158+
<TeleportStateProbe
159+
hostName='overlay-composer'
160+
id='registration-1'
161+
testID='teleport-state'
162+
/>
163+
</>,
92164
);
165+
166+
act(() => {
167+
stateStore.setClosingPortalLayout('overlay-composer', 'registration-1', BASE_RECT);
168+
stateStore.openOverlay('message-1');
169+
stateStore.closeOverlay();
170+
});
171+
flushAnimationFrameQueue();
172+
173+
expect(screen.getByTestId('teleport-state')).toHaveTextContent('false');
174+
expect(findPortalNode(toJSON(), 'composer-portal')?.props?.hostName).toBeUndefined();
93175
});
94176

95-
it('clears its registered layout entry when it unmounts', () => {
96-
const { unmount } = render(
97-
<PortalWhileClosingView portalHostName='overlay-composer' portalName='cleanup-portal'>
98-
<Text>Cleanup</Text>
99-
</PortalWhileClosingView>,
177+
it('clears its registration from the real store when it unmounts', () => {
178+
const { rerender } = render(
179+
<>
180+
<PortalWhileClosingView portalHostName='overlay-composer' portalName='composer-portal'>
181+
<Text>Composer</Text>
182+
</PortalWhileClosingView>
183+
<TeleportStateProbe
184+
hostName='overlay-composer'
185+
id='registration-1'
186+
testID='teleport-state'
187+
/>
188+
</>,
100189
);
101190

102191
act(() => {
103-
unmount();
192+
stateStore.setClosingPortalLayout('overlay-composer', 'registration-1', BASE_RECT);
193+
stateStore.openOverlay('message-1');
194+
stateStore.closeOverlay();
104195
});
196+
flushAnimationFrameQueue();
197+
198+
expect(screen.getByTestId('teleport-state')).toHaveTextContent('true');
105199

106-
expect(mockedClearClosingPortalLayout).toHaveBeenCalledWith(
107-
'overlay-composer',
108-
'registration-1',
200+
rerender(
201+
<TeleportStateProbe
202+
hostName='overlay-composer'
203+
id='registration-1'
204+
testID='teleport-state'
205+
/>,
109206
);
207+
208+
expect(screen.getByTestId('teleport-state')).toHaveTextContent('false');
110209
});
111210
});

0 commit comments

Comments
 (0)