Skip to content

Commit df54cc7

Browse files
KrzysztofWojnarKrzysztof Wojnarkligarskikmichalikk
authored
chore(e2e): maintenance for automated tests after iOS 26.2 (#3430)
## Description Closes https://github.com/software-mansion/react-native-screens-labs/issues/540 Broad changes after iOS 26.2 and some by-the-way fixes or improvements. Not fixed tests: iOS tests for example Test2809 should be aligned later with an output of PR #3303. iOS tests for examples Test645 and Test649 are affected by issue [#566](https://github.com/software-mansion/react-native-screens-labs/issues/566) ## Changes Detox e2e tests. Increased test isolation. Bar back button technical selector depends now on ios version. Some android emulators my get a pop-up about using stylus which interrupts the test flow – it is disabled in the global config now. ## Checklist - [x] Included code example that can be used to test this change - [x] Updated TS types - [x] Updated documentation: <!-- For adding new props to native-stack --> - [x] https://github.com/software-mansion/react-native-screens/blob/main/guides/GUIDE_FOR_LIBRARY_AUTHORS.md - [x] https://github.com/software-mansion/react-native-screens/blob/main/native-stack/README.md - [x] https://github.com/software-mansion/react-native-screens/blob/main/src/types.tsx - [x] https://github.com/software-mansion/react-native-screens/blob/main/src/native-stack/types.tsx - [ ] Ensured that CI passes --------- Co-authored-by: Krzysztof Wojnar <krzysztof.wojnar@swmansion.com> Co-authored-by: Krzysztof Ligarski <63918941+kligarski@users.noreply.github.com> Co-authored-by: Konrad Michalik <konrad.michalik@swmansion.com>
1 parent 9bfc35c commit df54cc7

39 files changed

Lines changed: 678 additions & 528 deletions

.github/workflows/android-e2e-test-fabric.yml

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,18 @@
11
name: Test Android e2e - new architecture
22
on:
3+
pull_request:
4+
branches:
5+
- main
6+
paths:
7+
- '.github/workflows/android-e2e-test-fabric.yml'
8+
- 'package.json'
9+
- 'android/**'
10+
- 'common/**'
11+
- 'FabricExample/**'
12+
- 'src/**'
13+
push:
14+
branches:
15+
- main
316
workflow_dispatch:
417
jobs:
518
test:

.github/workflows/ios-e2e-test-fabric.yml

Lines changed: 17 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,24 @@
11
name: Test iOS e2e - new architecture
22
on:
3+
pull_request:
4+
branches:
5+
- main
6+
paths:
7+
- '.github/workflows/ios-e2e-test-fabric.yml'
8+
- 'RNScreens.podspec'
9+
- 'package.json'
10+
- 'ios/**'
11+
- 'common/**'
12+
- 'FabricExample/**'
13+
- 'src/**'
14+
push:
15+
branches:
16+
- main
317
workflow_dispatch:
418
jobs:
519
test:
6-
runs-on: macos-14
7-
timeout-minutes: 60
20+
runs-on: macos-26
21+
timeout-minutes: 90
822
env:
923
WORKING_DIRECTORY: FabricExample
1024
concurrency:
@@ -21,7 +35,7 @@ jobs:
2135
- name: Use latest stable Xcode
2236
uses: maxim-lobanov/setup-xcode@v1
2337
with:
24-
xcode-version: '16.1'
38+
xcode-version: '26.2'
2539
- name: Get Xcode version
2640
run: xcodebuild -version
2741
- name: Install AppleSimulatorUtils

FabricExample/.detoxrc.js

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,2 @@
11
const utils = require('../scripts/e2e/detox-utils.cjs');
22
module.exports = utils.commonDetoxConfigFactory('FabricExample');
3-

FabricExample/e2e/e2e-utils.ts

Lines changed: 18 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,20 @@
11
import { device, expect, element, by } from 'detox';
22

3-
export const describeIfiOS = device.getPlatform() === 'ios' ? describe : describe.skip;
3+
export const describeIfiOS =
4+
device.getPlatform() === 'ios' ? describe : describe.skip;
45

5-
export async function selectTestScreen(screenName: string) {
6-
await element(by.id('root-screen-switch-search-bar')).tap();
6+
async function scrollTo(id: string) {
7+
await waitFor(element(by.id(id)))
8+
.toBeVisible()
9+
.whileElement(by.id('root-screen-examples-scrollview'))
10+
.scroll(600, 'down', Number.NaN, 0.85);
11+
}
12+
13+
export async function selectIssueTestScreen(screenName: string) {
14+
await scrollTo('root-screen-issue-tests');
15+
await element(by.id('root-screen-issue-tests')).tap();
16+
17+
await waitFor(element(by.id('issue-tests-scrollview'))).toBeVisible();
718

819
if (device.getPlatform() === 'android') {
920
await element(by.label('Search')).tap();
@@ -12,15 +23,10 @@ export async function selectTestScreen(screenName: string) {
1223
// I don't know why element(by.type('androidx.appcompat.widget.SearchView.SearchAutoComplete'))
1324
// does not work even if it appears in view hierarchy returned by Detox in debug logging mode.
1425
await element(by.text('')).replaceText(screenName);
15-
16-
// Press back to hide the keyboard.
17-
// This is necessary to make sure that search results are not obstructed by the keyboard.
18-
// More details: https://github.com/software-mansion/react-native-screens/pull/2919
19-
await device.pressBack();
20-
} else {
21-
await element(by.type('UISearchBarTextField')).replaceText(screenName);
26+
} else if (device.getPlatform() === 'ios') {
27+
await element(by.traits(['searchField'])).typeText(screenName);
2228
}
2329

24-
await expect(element(by.id(`root-screen-tests-${screenName}`))).toBeVisible();
25-
await element(by.id(`root-screen-tests-${screenName}`)).tap();
30+
await expect(element(by.id(`issue-tests-${screenName}`))).toBeVisible();
31+
await element(by.id(`issue-tests-${screenName}`)).tap();
2632
}
Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
import { device, element, by } from 'detox';
2+
import isVersionEqualOrHigherThan from '../helpers/isVersionEqualOrHigherThan';
3+
4+
const { getIOSVersionNumber } = require('../../../scripts/e2e/ios-devices.js');
5+
6+
const IOS_BAR_BUTTON_TYPE = '_UIButtonBarButton';
7+
const backButtonElement = element(by.id('BackButton'));
8+
9+
export async function tapBarBackButton() {
10+
const platform = device.getPlatform();
11+
if (platform === 'ios') {
12+
return (await getIOSBackButton()).tap();
13+
} else if (platform === 'android') {
14+
return backButtonElement.tap();
15+
} else throw new Error(`Platform "${platform}" not supported`);
16+
}
17+
18+
async function getIOSBackButton() {
19+
const iosVersion = getIOSVersionNumber();
20+
if (isVersionEqualOrHigherThan(iosVersion, '26.0')) {
21+
const elementsByAttributes =
22+
(await backButtonElement.getAttributes()) as unknown as {
23+
elements: { className: string }[];
24+
};
25+
const elements = elementsByAttributes.elements;
26+
if (Array.isArray(elements)) {
27+
const uiBarButtonIndex = elements.findIndex(
28+
elem => elem.className === IOS_BAR_BUTTON_TYPE,
29+
);
30+
if (uiBarButtonIndex !== -1) {
31+
return backButtonElement.atIndex(uiBarButtonIndex);
32+
}
33+
}
34+
}
35+
return backButtonElement;
36+
}

FabricExample/e2e/examplesTests/bottomTabs.e2e.ts renamed to FabricExample/e2e/example-tests/bottomTabs.e2e.ts

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -82,7 +82,6 @@ describe('Bottom tabs and native stack', () => {
8282
element(by.id('bottom-tabs-B-more-details-button')),
8383
).toHaveLabel('More details 0');
8484

85-
8685
// TODO(kkafar): For some reason tapping the button here hangs test runner.
8786
// The app itself stays fully responsive & in valid state (the expectation
8887
// defined below should succeed).

FabricExample/e2e/examplesTests/events.e2e.ts renamed to FabricExample/e2e/example-tests/events.e2e.ts

Lines changed: 28 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -11,45 +11,40 @@ const pressBack = async () => {
1111
};
1212

1313
const awaitClassicalEventBehavior = async () => {
14-
if (device.getPlatform() === 'ios') {
15-
// The order of events in this test differs from Paper.
16-
// Please see https://github.com/software-mansion/react-native-screens/pull/2785 for details.
17-
await expect(
18-
element(by.text('9. Chats | transitionStart | closing')),
19-
).toExist();
20-
await expect(
21-
element(by.text('10. Privacy | transitionStart | closing')),
22-
).toExist();
23-
await expect(
24-
element(by.text('11. Main | transitionStart | opening')),
25-
).toExist();
26-
await expect(element(by.text('12. Privacy | beforeRemove'))).toExist();
27-
await expect(element(by.text('13. Chats | beforeRemove'))).toExist();
28-
await expect(
29-
element(by.text('14. Chats | transitionEnd | closing')),
30-
).toExist();
31-
await expect(
32-
element(by.text('15. Privacy | transitionEnd | closing')),
33-
).toExist();
34-
await expect(
35-
element(by.text('16. Main | transitionEnd | opening')),
36-
).toExist();
37-
} else {
38-
await expect(element(by.text('9. Privacy | beforeRemove'))).toExist();
39-
await expect(element(by.text('10. Chats | beforeRemove'))).toExist();
40-
await expect(
41-
element(by.text('11. Main | transitionStart | opening')),
42-
).toExist();
43-
await expect(
44-
element(by.text('12. Main | transitionEnd | opening')),
45-
).toExist();
14+
// The order of events in this test differs from Paper.
15+
// Please see https://github.com/software-mansion/react-native-screens/pull/2785 for details.
16+
const expectedEvents =
17+
device.getPlatform() === 'ios'
18+
? [
19+
'9. Chats | transitionStart | closing',
20+
'10. Privacy | transitionStart | closing',
21+
'11. Main | transitionStart | opening',
22+
'12. Privacy | beforeRemove',
23+
'13. Chats | beforeRemove',
24+
'14. Chats | transitionEnd | closing',
25+
'15. Privacy | transitionEnd | closing',
26+
'16. Main | transitionEnd | opening',
27+
]
28+
: [
29+
'9. Privacy | beforeRemove',
30+
'10. Chats | beforeRemove',
31+
'11. Main | transitionStart | opening',
32+
'12. Main | transitionEnd | opening',
33+
];
34+
35+
// Wait for the last event to appear before asserting event order
36+
await waitFor(element(by.text(expectedEvents.at(-1)!)))
37+
.toExist()
38+
.withTimeout(5000);
39+
40+
for (const expectedEventNotification of expectedEvents) {
41+
await expect(element(by.text(expectedEventNotification))).toExist();
4642
}
4743
};
4844

4945
describe('Events', () => {
5046
beforeEach(async () => {
5147
await device.reloadReactNative();
52-
// await device.launchApp({ newInstance: true });
5348

5449
await waitFor(element(by.id('root-screen-playground-Events')))
5550
.toBeVisible()

FabricExample/e2e/examplesTests/nativeBottomTabs.e2e.ts renamed to FabricExample/e2e/example-tests/nativeBottomTabs.e2e.ts

Lines changed: 5 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,38 +1,32 @@
11
import { device, expect, element, by } from 'detox';
2-
import { selectTestScreen } from '../e2e-utils';
2+
import { selectIssueTestScreen } from '../e2e-utils';
33

44
describe('Native BottomTabs', () => {
55
beforeAll(async () => {
66
await device.reloadReactNative();
77
});
88

99
it('should select TestBottomTabs', async () => {
10-
await selectTestScreen('TestBottomTabs');
10+
await selectIssueTestScreen('TestBottomTabs');
1111
});
1212

1313
it('should initially display first tab', async () => {
14-
await expect(
15-
element(by.id('tab-screen-1-id')),
16-
).toBeVisible();
14+
await expect(element(by.id('tab-screen-1-id'))).toBeVisible();
1715
});
1816

1917
it('should use tab id to navigate to third tab screen', async () => {
2018
const tabElement = element(by.id('tab-item-3-id'));
2119
await expect(tabElement).toBeVisible();
2220
await tabElement.tap();
2321

24-
await expect(
25-
element(by.id('tab-screen-3-id')),
26-
).toBeVisible();
22+
await expect(element(by.id('tab-screen-3-id'))).toBeVisible();
2723
});
2824

2925
it('should use tab label to navigate to second tab screen', async () => {
3026
const tabElement = element(by.label('Second Tab Item'));
3127
await expect(tabElement).toBeVisible();
3228
await tabElement.tap();
3329

34-
await expect(
35-
element(by.label('Second Tab Screen')),
36-
).toBeVisible();
30+
await expect(element(by.label('Second Tab Screen'))).toBeVisible();
3731
});
3832
});

FabricExample/e2e/examplesTests/simpleNativeStack.e2e.ts renamed to FabricExample/e2e/example-tests/simpleNativeStack.e2e.ts

File renamed without changes.

FabricExample/e2e/examplesTests/stackPresentation.e2e.ts renamed to FabricExample/e2e/example-tests/stackPresentation.e2e.ts

File renamed without changes.

0 commit comments

Comments
 (0)