Skip to content

Commit 95d5012

Browse files
iOS & Android: Migrate 29 Channel Settings tests from Rainforest to Detox (#9313)
1 parent 121980c commit 95d5012

Some content is hidden

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

44 files changed

+2084
-1108
lines changed

.github/workflows/e2e-ios-template.yml

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -115,7 +115,7 @@ jobs:
115115
id: generate-specs
116116
uses: ./.github/actions/generate-specs
117117
with:
118-
parallelism: 5
118+
parallelism: 10
119119
search_path: detox/e2e/test
120120
device_name: ${{ env.DEVICE_NAME }}
121121
device_os_version: ${{ env.DEVICE_OS_VERSION }}
@@ -244,8 +244,8 @@ jobs:
244244
# Open Simulator.app in background to speed up UI rendering
245245
open -a Simulator --args -CurrentDeviceUDID "$SIMULATOR_ID"
246246
247-
# Give Simulator.app time to fully initialize UI
248-
sleep 3
247+
# Give Simulator.app time to fully initialize UI (iOS 26.2 needs more time)
248+
sleep 5
249249
250250
echo "SIMULATOR_ID=$SIMULATOR_ID" >> $GITHUB_ENV
251251
@@ -319,9 +319,9 @@ jobs:
319319
tail -30 metro.log
320320
else
321321
echo "Metro is serving on http://localhost:8081"
322-
# Give Metro extra time to fully initialize for iOS 26.2
322+
# Give Metro extra time to fully initialize for iOS 26.2 (React Native bridge needs more time)
323323
echo "Allowing Metro to fully stabilize..."
324-
sleep 3
324+
sleep 8
325325
fi
326326
327327
cd detox

detox/.detoxrc.json

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -83,10 +83,10 @@
8383
"behavior": {
8484
"init": {
8585
"reinstallApp": false,
86-
"launchApp": true
86+
"launchApp": false
8787
},
8888
"cleanup": {
89-
"shutdownDevice": true
89+
"shutdownDevice": false
9090
}
9191
},
9292
"session": {

detox/README.md

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -181,4 +181,3 @@ To run Playbooks tests:
181181
For complete documentation, see:
182182
- [Wix Pilot Technical Overview](https://wix-pilot.com/docs/guides/technical-overview)
183183
- [Pilot Best Practices Guide](https://wix-pilot.com/docs/guides/pilot-best-practices)
184-

detox/e2e/config.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ const shard = process.env.CI_NODE_INDEX ? process.env.CI_NODE_INDEX : '';
66

77
module.exports = {
88
setupFilesAfterEnv: ['./test/setup.ts'],
9-
maxWorkers: 1,
9+
maxWorkers: process.env.CI ? 1 : 2,
1010
testSequencer: './custom_sequencer.js',
1111
testTimeout: process.env.LOW_BANDWIDTH_MODE === 'true' ? 240000 : 180000,
1212
rootDir: '.',

detox/e2e/support/adb_utils.ts

Lines changed: 0 additions & 80 deletions
This file was deleted.

detox/e2e/support/ui/component/alert.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,7 @@ class Alert {
4040
// alert buttons
4141
cancelButton = isAndroid() ? element(by.text('CANCEL')) : element(by.label('Cancel')).atIndex(1);
4242
confirmButton = isAndroid() ? element(by.text('CONFIRM')) : element(by.label('Confirm')).atIndex(1);
43+
doneButton = isAndroid() ? element(by.text('DONE')) : element(by.label('Done')).atIndex(1);
4344
deleteButton = isAndroid() ? element(by.text('DELETE')) : element(by.label('Delete')).atIndex(0);
4445
deleteScheduledMessageButton = isAndroid() ? element(by.text('DELETE')) : element(by.label('Delete')).atIndex(1);
4546
leaveButton = isAndroid() ? element(by.text('LEAVE')) : element(by.label('Leave')).atIndex(0);
Lines changed: 77 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,77 @@
1+
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
2+
// See LICENSE.txt for license information.
3+
4+
import {ProfilePicture} from '@support/ui/component';
5+
import {timeouts, wait} from '@support/utils';
6+
import {expect} from 'detox';
7+
8+
class AddMembersScreen {
9+
testID = {
10+
addMembersScreen: 'add_members.screen',
11+
searchInput: 'add_members.search_bar.search.input',
12+
userList: 'add_members.user_list',
13+
userItemPrefix: 'add_members.user_list.user_item.',
14+
addButton: 'add_members.add.button',
15+
addChannelMembersButton: 'add_members.selected.start.button',
16+
backButton: 'screen.back.button',
17+
tutorialTooltip: 'tutorial_highlight',
18+
};
19+
20+
addChannelMembersButton = element(by.id(this.testID.addChannelMembersButton));
21+
addMembersScreen = element(by.id(this.testID.addMembersScreen));
22+
searchInput = element(by.id(this.testID.searchInput));
23+
userList = element(by.id(this.testID.userList));
24+
addButton = element(by.id(this.testID.addButton));
25+
backButton = element(by.id(this.testID.backButton));
26+
tutorialTooltip = element(by.id(this.testID.tutorialTooltip));
27+
28+
getUserItem = (userId: string) => {
29+
return element(by.id(`${this.testID.userItemPrefix}${userId}.${userId}`));
30+
};
31+
32+
getUserItemProfilePicture = (userId: string) => {
33+
return element(ProfilePicture.getProfilePictureItemMatcher(this.testID.userItemPrefix, userId));
34+
};
35+
36+
getUserItemDisplayName = (userId: string) => {
37+
return element(by.id(`${this.testID.userItemPrefix}${userId}.${userId}.display_name`));
38+
};
39+
40+
toBeVisible = async () => {
41+
await waitFor(this.searchInput).toBeVisible().withTimeout(timeouts.TEN_SEC);
42+
return this.addMembersScreen;
43+
};
44+
45+
close = async () => {
46+
await this.backButton.tap();
47+
await expect(this.addMembersScreen).not.toBeVisible();
48+
};
49+
50+
dismissTutorial = async () => {
51+
try {
52+
const tutorialText = element(by.text('Long-press on an item to view a user\'s profile'));
53+
await waitFor(tutorialText).toBeVisible().withTimeout(timeouts.TWO_SEC);
54+
await tutorialText.tap();
55+
await wait(timeouts.HALF_SEC);
56+
} catch {
57+
// Tutorial not visible, continue
58+
}
59+
};
60+
61+
searchAndAddUser = async (username: string, userId: string) => {
62+
await this.searchInput.typeText(`${username}\n`);
63+
await wait(timeouts.TWO_SEC);
64+
65+
const userItem = this.getUserItem(userId);
66+
await waitFor(userItem).toBeVisible().withTimeout(timeouts.TEN_SEC);
67+
await userItem.tap();
68+
await wait(timeouts.ONE_SEC);
69+
70+
await waitFor(this.addChannelMembersButton).toBeVisible().withTimeout(timeouts.TEN_SEC);
71+
await this.addChannelMembersButton.tap();
72+
await wait(timeouts.TWO_SEC);
73+
};
74+
}
75+
76+
const addMembersScreen = new AddMembersScreen();
77+
export default addMembersScreen;

detox/e2e/support/ui/screen/channel.ts

Lines changed: 4 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ import {
1717
PostOptionsScreen,
1818
ThreadScreen,
1919
} from '@support/ui/screen';
20-
import {isAndroid, isIos, timeouts, wait} from '@support/utils';
20+
import {isIos, timeouts, wait} from '@support/utils';
2121
import {expect} from 'detox';
2222

2323
class ChannelScreen {
@@ -153,13 +153,13 @@ class ChannelScreen {
153153
dismissScheduledPostTooltip = async () => {
154154
// Try to close scheduled post tooltip if it exists (try both regular and admin account versions)
155155
try {
156-
await waitFor(this.scheduledPostTooltipCloseButton).toBeVisible().withTimeout(timeouts.ONE_SEC);
156+
await waitFor(this.scheduledPostTooltipCloseButton).toBeVisible().withTimeout(timeouts.FOUR_SEC);
157157
await this.scheduledPostTooltipCloseButton.tap();
158158
await wait(timeouts.HALF_SEC);
159159
} catch {
160160
// Try admin account version
161161
try {
162-
await waitFor(this.scheduledPostTooltipCloseButtonAdminAccount).toBeVisible().withTimeout(timeouts.ONE_SEC);
162+
await waitFor(this.scheduledPostTooltipCloseButtonAdminAccount).toBeVisible().withTimeout(timeouts.FOUR_SEC);
163163
await this.scheduledPostTooltipCloseButtonAdminAccount.tap();
164164
await wait(timeouts.HALF_SEC);
165165
} catch {
@@ -204,23 +204,6 @@ class ChannelScreen {
204204
}
205205
};
206206

207-
dismissKeyboard = async () => {
208-
// Explicitly dismiss keyboard before long press
209-
if (isAndroid()) {
210-
try {
211-
await device.pressBack();
212-
await wait(timeouts.THREE_SEC);
213-
} catch (error) {
214-
// Keyboard might not be open, continue
215-
}
216-
}
217-
if (isIos()) {
218-
// On iOS, tap outside the input area to dismiss keyboard
219-
await this.postInput.tapReturnKey();
220-
await wait(timeouts.TWO_SEC);
221-
}
222-
};
223-
224207
openPostOptionsFor = async (postId: string, text: string) => {
225208
const {postListPostItem} = this.getPostListPostItem(postId, text);
226209
await waitFor(postListPostItem).toBeVisible().withTimeout(timeouts.TEN_SEC);
@@ -243,7 +226,7 @@ class ChannelScreen {
243226
// # Post message
244227
await this.postInput.tap();
245228
await this.postInput.clearText();
246-
await this.postInput.typeText(message);
229+
await this.postInput.replaceText(`${message}\n`);
247230
await this.tapSendButton();
248231
await wait(timeouts.TWO_SEC);
249232
};

detox/e2e/support/ui/screen/channel_info.ts

Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -233,6 +233,58 @@ class ChannelInfoScreen {
233233
unarchivePublicChannel = async ({confirm = true} = {}) => {
234234
await this.unarchiveChannel(Alert.unarchivePublicChannelTitle, {confirm});
235235
};
236+
237+
copyChannelHeader = async (headerText: string) => {
238+
// Long press on header text
239+
await element(by.text(headerText)).longPress();
240+
241+
// Wait for bottom sheet
242+
await waitFor(element(by.id('channel_info.extra.header.bottom_sheet.copy_header_text'))).
243+
toBeVisible().
244+
withTimeout(timeouts.TWO_SEC);
245+
246+
// Tap copy option (actual copy action)
247+
await element(by.id('channel_info.extra.header.bottom_sheet.copy_header_text')).tap();
248+
};
249+
250+
cancelCopyChannelHeader = async (headerText: string) => {
251+
// Long press on header text
252+
await element(by.text(headerText)).longPress();
253+
254+
// Wait for bottom sheet
255+
await waitFor(element(by.id('channel_info.extra.header.bottom_sheet.copy_header_text'))).
256+
toBeVisible().
257+
withTimeout(timeouts.TWO_SEC);
258+
259+
// Cancel
260+
await element(by.id('channel_info.extra.header.bottom_sheet.cancel')).tap();
261+
};
262+
263+
copyChannelPurpose = async (purposeText: string) => {
264+
// Long press on purpose text
265+
await element(by.text(purposeText)).longPress();
266+
267+
// Wait for bottom sheet
268+
await waitFor(element(by.id('channel_info.title.public_private.bottom_sheet.copy_purpose'))).
269+
toBeVisible().
270+
withTimeout(timeouts.TWO_SEC);
271+
272+
// Tap copy option
273+
await element(by.id('channel_info.title.public_private.bottom_sheet.copy_purpose')).tap();
274+
};
275+
276+
cancelCopyChannelPurpose = async (purposeText: string) => {
277+
// Long press on purpose text
278+
await element(by.text(purposeText)).longPress();
279+
280+
// Wait for bottom sheet
281+
await waitFor(element(by.id('channel_info.title.public_private.bottom_sheet.copy_purpose'))).
282+
toBeVisible().
283+
withTimeout(timeouts.TWO_SEC);
284+
285+
// Cancel
286+
await element(by.id('channel_info.title.public_private.bottom_sheet.cancel')).tap();
287+
};
236288
}
237289

238290
const channelInfoScreen = new ChannelInfoScreen();

detox/e2e/support/ui/screen/create_direct_message.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -77,7 +77,7 @@ class CreateDirectMessageScreen {
7777
await ChannelListScreen.headerPlusButton.tap();
7878
await wait(timeouts.ONE_SEC);
7979
await ChannelListScreen.openDirectMessageItem.tap();
80-
await wait(timeouts.TEN_SEC);
80+
await wait(timeouts.FOUR_SEC);
8181
return this.toBeVisible();
8282
};
8383

0 commit comments

Comments
 (0)