Skip to content

Commit

Permalink
feat: Migrate example apps to use react-navigation (software-mansion#…
Browse files Browse the repository at this point in the history
…2084)

## Description

This PR migrates our example apps to use react-navigation. Because we're
right now using react-navigation as a submodule (and we intent to remove
native-stack v5 from codebase), it's mandatory to migrate all of the
example apps to @react-navigation/native-stack. To do this, I've moved
all imports for creating navigator to use native-stack v6 and changed
the naming of the props. Right now all of the examples should work, but
it's important to build `lib` directories from submodule (using `yarn
build`) beforehand.

## Changes

- Moved all navigators to use `@react-navigation/native-stack`
- Changed naming of the props to match native-stack v6
- Changed E2E tests a little

## Test code and steps to reproduce
You can just run example apps to test this change.

## Checklist

- [x] Ensured that CI passes
  • Loading branch information
tboba authored and ja1ns committed Oct 9, 2024
1 parent 995aae9 commit 5748588
Show file tree
Hide file tree
Showing 208 changed files with 1,212 additions and 1,339 deletions.
6 changes: 6 additions & 0 deletions .github/workflows/android-e2e-test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -38,8 +38,14 @@ jobs:
- name: Install root node dependencies
run: yarn install && yarn submodules
- name: Install node dependencies
id: install_deps
working-directory: ${{ env.WORKING_DIRECTORY }}
continue-on-error: true
run: yarn install
- if: steps.install_deps.outcome == 'failure'
name: Reinstall node dependencies
working-directory: ${{ env.WORKING_DIRECTORY }}
run: yarn
- name: Build app
working-directory: ${{ env.WORKING_DIRECTORY }}
run: yarn build-e2e-android
Expand Down
7 changes: 2 additions & 5 deletions Example/App.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ import {
} from 'react-native';
import { NavigationContainer } from '@react-navigation/native';
import { StackNavigationProp } from '@react-navigation/stack';
import { createNativeStackNavigator } from 'react-native-screens/native-stack';
import { createNativeStackNavigator } from '@react-navigation/native-stack';
import RNRestart from 'react-native-restart';

import { ListItem, SettingsSwitch } from './src/shared';
Expand Down Expand Up @@ -161,10 +161,7 @@ const ExampleApp = (): JSX.Element => (
<GestureHandlerRootView style={{ flex: 1 }}>
<GestureDetectorProvider>
<NavigationContainer>
<Stack.Navigator
screenOptions={{
direction: I18nManager.isRTL ? 'rtl' : 'ltr',
}}>
<Stack.Navigator>
<Stack.Screen
name="Main"
options={{ title: '📱 React Native Screens Examples' }}
Expand Down
68 changes: 36 additions & 32 deletions Example/e2e/examplesTests/bottomTabs.e2e.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,75 +12,79 @@ describe('Bottom tabs and native stack', () => {
await element(by.id('root-screen-example-BottomTabsAndStack')).tap();

await expect(
element(by.id('bottom-tabs-more-details-button'))
element(by.id('bottom-tabs-A-more-details-button'))
).toBeVisible();
});

it('should go to details screen', async () => {
await element(by.id('root-screen-example-BottomTabsAndStack')).tap();
await element(by.id('bottom-tabs-more-details-button')).tap();
await expect(element(by.id('bottom-tabs-more-details-button'))).toHaveLabel(
'More details 1'
);
await element(by.id('bottom-tabs-A-more-details-button')).tap();
await expect(
element(by.id('bottom-tabs-A-more-details-button'))
).toHaveLabel('More details 1');
});

it('should go to details screen and back', async () => {
await element(by.id('root-screen-example-BottomTabsAndStack')).tap();
await element(by.id('bottom-tabs-more-details-button')).tap();
await expect(element(by.id('bottom-tabs-more-details-button'))).toHaveLabel(
'More details 1'
);
await element(by.id('bottom-tabs-A-more-details-button')).tap();
await expect(
element(by.id('bottom-tabs-A-more-details-button'))
).toHaveLabel('More details 1');
if (device.getPlatform() === 'ios') {
await element(by.type('_UIButtonBarButton')).tap();
} else {
await device.pressBack();
}
await expect(element(by.id('bottom-tabs-more-details-button'))).toHaveLabel(
'More details 0'
);
await expect(
element(by.id('bottom-tabs-A-more-details-button'))
).toHaveLabel('More details 0');
});

it('should go between tabs', async () => {
await element(by.id('root-screen-example-BottomTabsAndStack')).tap();

await element(by.id('bottom-tabs-B-tab')).tap();
await expect(element(by.id('bottom-tabs-header-right-id'))).toHaveText('B');
await expect(element(by.id('bottom-tabs-B-header-right-id'))).toHaveText(
'B'
);

await element(by.id('bottom-tabs-A-tab')).tap();
await expect(element(by.id('bottom-tabs-header-right-id'))).toHaveText('A');
await expect(element(by.id('bottom-tabs-A-header-right-id'))).toHaveText(
'A'
);
});

it('should go to first screen on double tap on a tab', async () => {
await element(by.id('root-screen-example-BottomTabsAndStack')).tap();

await element(by.id('bottom-tabs-more-details-button')).tap();
await expect(element(by.id('bottom-tabs-more-details-button'))).toHaveLabel(
'More details 1'
);
await element(by.id('bottom-tabs-A-more-details-button')).tap();
await expect(
element(by.id('bottom-tabs-A-more-details-button'))
).toHaveLabel('More details 1');

await element(by.id('bottom-tabs-A-tab')).multiTap(2);
await expect(element(by.id('bottom-tabs-more-details-button'))).toHaveLabel(
'More details 0'
);
await expect(
element(by.id('bottom-tabs-A-more-details-button'))
).toHaveLabel('More details 0');
});

it('should keep stack state on tab change', async () => {
await element(by.id('root-screen-example-BottomTabsAndStack')).tap();

await element(by.id('bottom-tabs-more-details-button')).tap();
await element(by.id('bottom-tabs-more-details-button')).tap();
await expect(element(by.id('bottom-tabs-more-details-button'))).toHaveLabel(
'More details 2'
);
await element(by.id('bottom-tabs-A-more-details-button')).tap();
await element(by.id('bottom-tabs-A-more-details-button')).tap();
await expect(
element(by.id('bottom-tabs-A-more-details-button'))
).toHaveLabel('More details 2');

await element(by.id('bottom-tabs-B-tab')).tap();
await expect(element(by.id('bottom-tabs-more-details-button'))).toHaveLabel(
'More details 0'
);
await expect(
element(by.id('bottom-tabs-B-more-details-button'))
).toHaveLabel('More details 0');
await element(by.id('bottom-tabs-A-tab')).tap();

await expect(element(by.id('bottom-tabs-more-details-button'))).toHaveLabel(
'More details 2'
);
await expect(
element(by.id('bottom-tabs-A-more-details-button'))
).toHaveLabel('More details 2');
});
});
143 changes: 56 additions & 87 deletions Example/e2e/examplesTests/events.e2e.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,49 @@
import { device, expect, element, by } from 'detox';

const pressBack = async () => {
if (device.getPlatform() === 'android') {
await device.pressBack();
} else if (device.getPlatform() === 'ios') {
await element(by.traits(['button']))
.atIndex(0)
.tap();
}
};

const awaitClassicalEventBehavior = async () => {
if (device.getPlatform() === 'ios') {
await expect(
element(by.text('9. Chats | transitionStart | closing'))
).toExist();
await expect(
element(by.text('10. Privacy | transitionStart | closing'))
).toExist();
await expect(
element(by.text('11. Main | transitionStart | opening'))
).toExist();
await expect(
element(by.text('12. Chats | transitionEnd | closing'))
).toExist();
await expect(
element(by.text('13. Privacy | transitionEnd | closing'))
).toExist();
await expect(element(by.text('14. Privacy | beforeRemove'))).toExist();
await expect(element(by.text('15. Chats | beforeRemove'))).toExist();
await expect(
element(by.text('16. Main | transitionEnd | opening'))
).toExist();
} else {
await expect(element(by.text('9. Privacy | beforeRemove'))).toExist();
await expect(element(by.text('10. Chats | beforeRemove'))).toExist();
await expect(
element(by.text('11. Main | transitionStart | opening'))
).toExist();
await expect(
element(by.text('12. Main | transitionEnd | opening'))
).toExist();
}
};

describe('Events', () => {
beforeEach(async () => {
await device.reloadReactNative();
Expand Down Expand Up @@ -39,24 +83,7 @@ describe('Events', () => {
).tap();
}

await expect(
element(by.text('9. Chats | transitionStart | closing'))
).toExist();
await expect(
element(by.text('10. Privacy | transitionStart | closing'))
).toExist();
await expect(
element(by.text('11. Main | transitionStart | opening'))
).toExist();
await expect(
element(by.text('12. Chats | transitionEnd | closing'))
).toExist();
await expect(
element(by.text('13. Privacy | transitionEnd | closing'))
).toExist();
await expect(
element(by.text('14. Main | transitionEnd | opening'))
).toExist();
await awaitClassicalEventBehavior();
});

it('should use "none" animation, go back from Chats using header button and run opening & closing events in correct order ', async () => {
Expand All @@ -75,24 +102,7 @@ describe('Events', () => {
).tap();
}

await expect(
element(by.text('9. Chats | transitionStart | closing'))
).toExist();
await expect(
element(by.text('10. Privacy | transitionStart | closing'))
).toExist();
await expect(
element(by.text('11. Main | transitionStart | opening'))
).toExist();
await expect(
element(by.text('12. Chats | transitionEnd | closing'))
).toExist();
await expect(
element(by.text('13. Privacy | transitionEnd | closing'))
).toExist();
await expect(
element(by.text('14. Main | transitionEnd | opening'))
).toExist();
await awaitClassicalEventBehavior();
});

it('should use "slide_from_bottom" animation, go to Chats and run opening & closing events in correct order ', async () => {
Expand Down Expand Up @@ -139,83 +149,42 @@ describe('Events', () => {
).tap();
}

await expect(
element(by.text('9. Chats | transitionStart | closing'))
).toExist();
await expect(
element(by.text('10. Privacy | transitionStart | closing'))
).toExist();
await expect(
element(by.text('11. Main | transitionStart | opening'))
).toExist();
await expect(
element(by.text('12. Chats | transitionEnd | closing'))
).toExist();
await expect(
element(by.text('13. Privacy | transitionEnd | closing'))
).toExist();
await expect(
element(by.text('14. Main | transitionEnd | opening'))
).toExist();
await awaitClassicalEventBehavior();
});

it('[Android] should go back from Chats using native way and run opening & closing events in correct order ', async () => {
// swipe to go back doesn't seem to work on iOS
if (device.getPlatform() !== 'android') return;

it('should go back from Chats using native way and run opening & closing events in correct order ', async () => {
await element(by.id('root-screen-playground-Events')).tap();

await element(by.id('events-go-to-chats')).tap();

await device.pressBack();
await pressBack();

await expect(
element(by.text('9. Main | transitionStart | opening'))
).toExist();
await expect(
element(by.text('10. Main | transitionEnd | opening'))
).toExist();
await awaitClassicalEventBehavior();
});

it('[Android] should use "none" animation, go back from Chats using native way and run opening & closing events in correct order ', async () => {
// swipe to go back doesn't seem to work on iOS
if (device.getPlatform() !== 'android') return;

it('should use "none" animation, go back from Chats using native way and run opening & closing events in correct order ', async () => {
await element(by.id('root-screen-playground-Events')).tap();

await element(by.id('events-stack-animation-picker')).tap();
await element(by.id('stack-animation-none')).tap();

await element(by.id('events-go-to-chats')).tap();

await device.pressBack();
await pressBack();

await expect(
element(by.text('9. Main | transitionStart | opening'))
).toExist();
await expect(
element(by.text('10. Main | transitionEnd | opening'))
).toExist();
await awaitClassicalEventBehavior();
});

it('[Android] should use "slide_from_bottom" animation, go back from Chats using native way and run opening & closing events in correct order ', async () => {
// swipe to go back doesn't seem to work on iOS
if (device.getPlatform() !== 'android') return;

it('should use "slide_from_bottom" animation, go back from Chats using native way and run opening & closing events in correct order ', async () => {
await element(by.id('root-screen-playground-Events')).tap();

await element(by.id('events-stack-animation-picker')).tap();
await element(by.id('stack-animation-slide_from_bottom')).tap();

await element(by.id('events-go-to-chats')).tap();

await device.pressBack();
await pressBack();

await expect(
element(by.text('9. Main | transitionStart | opening'))
).toExist();
await expect(
element(by.text('10. Main | transitionEnd | opening'))
).toExist();
await awaitClassicalEventBehavior();
});
});
Loading

0 comments on commit 5748588

Please sign in to comment.