Skip to content

Commit 4290006

Browse files
kmichalikkkkafar
andauthored
chore: Refactor library examples & tests (#3528)
Closes software-mansion/react-native-screens-labs#781 ## Description This PR is the first in series of example app refactors, as discussed in software-mansion/react-native-screens-labs#759. Two new test categories are added: Single Feature Tests and Component Integration Tests. The former is meant to test separately each single prop exposed by the library, and the latter is meant to test interactions between multiple props / multiple components. This distinction, however, is sometimes blurry, and the membership to each group will be decided on per-case basis. Let's talk over some examples in the discussion below. ## Changes Added Two entries to the Examples list: Single Feature Tests, Component Integration Tests. Added a way to quickly write simple tests - BottomTabsConfigProvider, StackConfigProvider - they provide context for `useStackConfig`/`useBottomTabsConfig` and `useDispatchStackConfig`/`useDispatchBottomTabsConfig` - it uses `useReducer` with `useContext` under the hood to configure single feature under test by passing a config object "diff" - `BottomTabsAutoconfig`, `StackAutoconfig` - Uses the contex providers above to apply config diff to the respective component See the usage in the examples created for this PR. tl;dr: You provide a ConfigScreen that uses dispatch() for configuration, define a simple app with XAutoconfig wrapped in XConfigProvider, and it should work. ## Test plan Click through the new tests & check if they are working. This is a preparatory commit, so there is little to test. ## Checklist n/a --------- Co-authored-by: Kacper Kafara <kacper.kafara@swmansion.com>
1 parent 92a99fc commit 4290006

File tree

309 files changed

+1712
-323
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

309 files changed

+1712
-323
lines changed

apps/Example.tsx

Lines changed: 89 additions & 138 deletions
Original file line numberDiff line numberDiff line change
@@ -6,11 +6,7 @@ import {
66
Platform,
77
useColorScheme,
88
} from 'react-native';
9-
import {
10-
NavigationContainer,
11-
NavigationIndependentTree,
12-
useTheme,
13-
} from '@react-navigation/native';
9+
import { NavigationContainer, useTheme } from '@react-navigation/native';
1410
import { StackNavigationProp } from '@react-navigation/stack';
1511
import { createNativeStackNavigator } from '@react-navigation/native-stack';
1612
import RNRestart from 'react-native-restart';
@@ -34,9 +30,15 @@ import BarButtonItems from './src/screens/BarButtonItems';
3430

3531
import { GestureDetectorProvider } from 'react-native-screens/gesture-handler';
3632
import { GestureHandlerRootView } from 'react-native-gesture-handler';
33+
import {
34+
ScreensDarkTheme,
35+
ScreensLightTheme,
36+
} from './src/shared/styling/adapter/react-navigation';
3737

38-
import * as Tests from './src/tests';
39-
import { ScreensDarkTheme, ScreensLightTheme } from './src/shared/styling/adapter/react-navigation';
38+
import IssueTestsScreen from './src/tests/IssueTestsScreen';
39+
import SingleFeatureTests from './src/tests/single-feature-tests';
40+
import ComponentIntegrationTests from './src/tests/component-integration-tests';
41+
import { SafeAreaView } from 'react-native-safe-area-context';
4042

4143
function isPlatformReady(name: keyof typeof SCREENS) {
4244
if (Platform.isTV) {
@@ -46,16 +48,12 @@ function isPlatformReady(name: keyof typeof SCREENS) {
4648
return true;
4749
}
4850

49-
function isTestSectionEnabled() {
50-
return true;
51-
}
52-
5351
const SCREENS: Record<
5452
string,
5553
{
5654
title: string;
5755
component: () => React.JSX.Element;
58-
type: 'example' | 'playground' | 'test';
56+
type: 'example' | 'playground';
5957
isTVOSReady?: boolean;
6058
}
6159
> = {
@@ -135,56 +133,16 @@ const SCREENS: Record<
135133
},
136134
};
137135

138-
if (isTestSectionEnabled()) {
139-
Object.keys(Tests).forEach(testName => {
140-
SCREENS[testName] = {
141-
title: testName,
142-
component: () => {
143-
const TestComponent = Tests[testName as keyof typeof Tests];
144-
return (
145-
<NavigationIndependentTree>
146-
<TestComponent />
147-
</NavigationIndependentTree>
148-
);
149-
},
150-
type: 'test',
151-
};
152-
});
153-
}
154-
155136
const screens = Object.keys(SCREENS);
156137
const examples = screens.filter(name => SCREENS[name].type === 'example');
157138
const playgrounds = screens.filter(name => SCREENS[name].type === 'playground');
158-
const issueRegex = /Test(?<issue>\d+)(?<case>.*)/
159-
const tests = isTestSectionEnabled()
160-
? screens
161-
.filter(name => SCREENS[name].type === 'test')
162-
.sort((name1, name2) => {
163-
const spec1 = issueRegex.exec(name1)?.groups
164-
const spec2 = issueRegex.exec(name2)?.groups
165-
166-
const testNumber1 = parseInt(spec1?.issue as string)
167-
const testNumber2 = parseInt(spec2?.issue as string)
168-
169-
if (Number.isNaN(testNumber1) && Number.isNaN(testNumber2)) {
170-
return 0;
171-
} else if (Number.isNaN(testNumber1)) {
172-
return 1;
173-
} else if (Number.isNaN(testNumber2)) {
174-
return -1;
175-
} else if (testNumber1 !== testNumber2) {
176-
return testNumber1 - testNumber2;
177-
} else if ((spec1?.case as string) < (spec2?.case as string)) {
178-
return -1;
179-
} else {
180-
return (spec1?.case as string) > (spec2?.case as string) ? 1 : 0;
181-
}
182-
})
183-
: [];
184139

185140
type RootStackParamList = {
186141
Main: undefined;
187142
Tests: undefined;
143+
SingleFeatureTests: undefined;
144+
ComponentIntegrationTests: undefined;
145+
IssueTests: undefined;
188146
} & {
189147
[P in keyof typeof SCREENS]: undefined;
190148
};
@@ -198,96 +156,69 @@ interface MainScreenProps {
198156
const MainScreen = ({ navigation }: MainScreenProps): React.JSX.Element => {
199157
const { toggleTheme } = useContext(ThemeToggle);
200158
const isDark = useTheme().dark;
201-
const [searchQuery, setSearchQuery] = React.useState('');
202-
const [searchBarEnabled, setSearchBarEnabled] = React.useState(false);
203-
204-
React.useLayoutEffect(() => {
205-
if (searchBarEnabled) {
206-
navigation.setOptions({
207-
headerSearchBarOptions: {
208-
onChangeText: (event) => setSearchQuery(event.nativeEvent.text),
209-
},
210-
});
211-
} else {
212-
setSearchQuery('');
213-
navigation.setOptions({
214-
headerSearchBarOptions: undefined,
215-
});
216-
}
217-
}, [navigation, searchBarEnabled]);
218-
219-
const searchFilter = React.useCallback(
220-
(name: string) =>
221-
searchQuery === '' ||
222-
name.toLowerCase().includes(searchQuery.toLowerCase()),
223-
[searchQuery],
224-
);
225-
226-
const filteredExamples = examples.filter(searchFilter);
227-
const filteredPlaygrounds = playgrounds.filter(searchFilter);
228-
const filteredTests = tests.filter(searchFilter);
229159

230160
return (
231-
<ScrollView testID="root-screen-examples-scrollview" contentInsetAdjustmentBehavior="automatic">
232-
<SettingsSwitch
233-
style={styles.switch}
234-
label="Right to left"
235-
value={I18nManager.isRTL}
236-
onValueChange={() => {
237-
I18nManager.forceRTL(!I18nManager.isRTL);
238-
RNRestart.Restart();
239-
}}
240-
testID="root-screen-switch-rtl"
241-
/>
242-
<SettingsSwitch
243-
style={styles.switch}
244-
label="Dark mode"
245-
value={isDark}
246-
onValueChange={toggleTheme}
247-
/>
248-
<SettingsSwitch
249-
style={styles.switch}
250-
label="Search bar"
251-
value={searchBarEnabled}
252-
onValueChange={() => setSearchBarEnabled(!searchBarEnabled)}
253-
testID="root-screen-switch-search-bar"
254-
/>
255-
<ThemedText style={styles.label} testID="root-screen-examples-header">
256-
Examples
257-
</ThemedText>
258-
{filteredExamples.map(name => (
259-
<ListItem
260-
key={name}
261-
testID={`root-screen-example-${name}`}
262-
title={SCREENS[name].title}
263-
onPress={() => navigation.navigate(name)}
264-
disabled={!isPlatformReady(name)}
161+
<SafeAreaView edges={{ top: 'off', bottom: 'maximum' }}>
162+
<ScrollView testID="root-screen-examples-scrollview">
163+
<SettingsSwitch
164+
style={styles.switch}
165+
label="Right to left"
166+
value={I18nManager.isRTL}
167+
onValueChange={() => {
168+
I18nManager.forceRTL(!I18nManager.isRTL);
169+
RNRestart.Restart();
170+
}}
171+
testID="root-screen-switch-rtl"
265172
/>
266-
))}
267-
<ThemedText style={styles.label}>Playgrounds</ThemedText>
268-
{filteredPlaygrounds.map(name => (
269-
<ListItem
270-
key={name}
271-
testID={`root-screen-playground-${name}`}
272-
title={SCREENS[name].title}
273-
onPress={() => navigation.navigate(name)}
274-
disabled={!isPlatformReady(name)}
173+
<SettingsSwitch
174+
style={styles.switch}
175+
label="Dark mode"
176+
value={isDark}
177+
onValueChange={toggleTheme}
275178
/>
276-
))}
277-
{isTestSectionEnabled() && (
278-
<ThemedText style={styles.label}>Tests</ThemedText>
279-
)}
280-
{isTestSectionEnabled() &&
281-
filteredTests.map(name => (
179+
<ThemedText style={styles.label} testID="root-screen-examples-header">
180+
Examples
181+
</ThemedText>
182+
{examples.map(name => (
282183
<ListItem
283184
key={name}
284-
testID={`root-screen-tests-${name}`}
185+
testID={`root-screen-example-${name}`}
285186
title={SCREENS[name].title}
286187
onPress={() => navigation.navigate(name)}
287-
disabled={false}
188+
disabled={!isPlatformReady(name)}
288189
/>
289190
))}
290-
</ScrollView>
191+
<ThemedText style={styles.label}>Playgrounds</ThemedText>
192+
{playgrounds.map(name => (
193+
<ListItem
194+
key={name}
195+
testID={`root-screen-playground-${name}`}
196+
title={SCREENS[name].title}
197+
onPress={() => navigation.navigate(name)}
198+
disabled={!isPlatformReady(name)}
199+
/>
200+
))}
201+
<ThemedText style={styles.label}>Tests</ThemedText>
202+
<ListItem
203+
key="SingleFeatureTests"
204+
testID="root-screen-single-feature-tests"
205+
title="Single Feature Tests"
206+
onPress={() => navigation.navigate('SingleFeatureTests')}
207+
/>
208+
<ListItem
209+
key="ComponentIntegrationTests"
210+
testID="root-screen-component-integration-tests"
211+
title="Component Integration Tests"
212+
onPress={() => navigation.navigate('ComponentIntegrationTests')}
213+
/>
214+
<ListItem
215+
key="IssueTests"
216+
testID="root-screen-issue-tests"
217+
title="Issue Tests"
218+
onPress={() => navigation.navigate('IssueTests')}
219+
/>
220+
</ScrollView>
221+
</SafeAreaView>
291222
);
292223
};
293224

@@ -305,14 +236,16 @@ const ExampleApp = (): React.JSX.Element => {
305236
<GestureHandlerRootView style={{ flex: 1 }}>
306237
<GestureDetectorProvider>
307238
<ThemeToggle.Provider value={{ toggleTheme }}>
308-
<NavigationContainer theme={isDark ? ScreensDarkTheme : ScreensLightTheme}>
239+
<NavigationContainer
240+
theme={isDark ? ScreensDarkTheme : ScreensLightTheme}>
309241
<Stack.Navigator
310242
screenOptions={{ statusBarStyle: isDark ? 'light' : 'dark' }}>
311243
<Stack.Screen
312244
name="Main"
313245
options={{
314-
title: `${Platform.isTV ? '📺' : '📱'
315-
} React Native Screens Examples`,
246+
title: `${
247+
Platform.isTV ? '📺' : '📱'
248+
} React Native Screens Examples`,
316249
}}
317250
component={MainScreen}
318251
/>
@@ -324,6 +257,24 @@ const ExampleApp = (): React.JSX.Element => {
324257
options={{ headerShown: false }}
325258
/>
326259
))}
260+
<Stack.Screen
261+
key="SingleFeatureTests"
262+
name="SingleFeatureTests"
263+
options={{ headerShown: false }}
264+
component={SingleFeatureTests}
265+
/>
266+
<Stack.Screen
267+
key="ComponentIntegrationTests"
268+
name="ComponentIntegrationTests"
269+
options={{ headerShown: false }}
270+
component={ComponentIntegrationTests}
271+
/>
272+
<Stack.Screen
273+
key="IssueTests"
274+
name="IssueTests"
275+
options={{ headerShown: false }}
276+
component={IssueTestsScreen}
277+
/>
327278
</Stack.Navigator>
328279
</NavigationContainer>
329280
</ThemeToggle.Provider>

0 commit comments

Comments
 (0)