diff --git a/app/components/channel_icon.js b/app/components/channel_icon.js
index 4e3b2f3a1..e7d26299c 100644
--- a/app/components/channel_icon.js
+++ b/app/components/channel_icon.js
@@ -26,6 +26,7 @@ export default class ChannelIcon extends React.PureComponent {
type: PropTypes.string.isRequired,
isArchived: PropTypes.bool.isRequired,
isBot: PropTypes.bool.isRequired,
+ testID: PropTypes.string,
};
static defaultProps = {
@@ -48,6 +49,7 @@ export default class ChannelIcon extends React.PureComponent {
type,
isArchived,
isBot,
+ testID,
} = this.props;
const style = getStyleSheet(theme);
@@ -84,7 +86,7 @@ export default class ChannelIcon extends React.PureComponent {
);
} else if (isBot) {
@@ -92,7 +94,7 @@ export default class ChannelIcon extends React.PureComponent {
);
} else if (hasDraft) {
@@ -100,7 +102,7 @@ export default class ChannelIcon extends React.PureComponent {
);
} else if (type === General.OPEN_CHANNEL) {
@@ -108,7 +110,7 @@ export default class ChannelIcon extends React.PureComponent {
);
} else if (type === General.PRIVATE_CHANNEL) {
@@ -116,7 +118,7 @@ export default class ChannelIcon extends React.PureComponent {
);
} else if (type === General.GM_CHANNEL) {
@@ -125,7 +127,7 @@ export default class ChannelIcon extends React.PureComponent {
{membersCount}
@@ -138,7 +140,7 @@ export default class ChannelIcon extends React.PureComponent {
);
break;
@@ -147,7 +149,7 @@ export default class ChannelIcon extends React.PureComponent {
);
break;
@@ -156,7 +158,7 @@ export default class ChannelIcon extends React.PureComponent {
);
break;
@@ -165,7 +167,7 @@ export default class ChannelIcon extends React.PureComponent {
);
break;
diff --git a/app/components/custom_list/user_list_row/__snapshots__/user_list_test.test.js.snap b/app/components/custom_list/user_list_row/__snapshots__/user_list_test.test.js.snap
index d190621d7..64236e85b 100644
--- a/app/components/custom_list/user_list_row/__snapshots__/user_list_test.test.js.snap
+++ b/app/components/custom_list/user_list_row/__snapshots__/user_list_test.test.js.snap
@@ -15,6 +15,7 @@ exports[`UserListRow should match snapshot 1`] = `
enabled={true}
id="21345"
onPress={[Function]}
+ testID="custom_list.user_item"
>
@user
@@ -147,6 +150,7 @@ exports[`UserListRow should match snapshot for currentUser with (you) populated
enabled={true}
id="21345"
onPress={[Function]}
+ testID="custom_list.user_item"
>
@user
@@ -420,6 +429,7 @@ exports[`UserListRow should match snapshot for guest user 1`] = `
enabled={true}
id="21345"
onPress={[Function]}
+ testID="custom_list.user_item"
>
@user
diff --git a/app/components/custom_list/user_list_row/user_list_row.js b/app/components/custom_list/user_list_row/user_list_row.js
index a3af41feb..e42c0bde7 100644
--- a/app/components/custom_list/user_list_row/user_list_row.js
+++ b/app/components/custom_list/user_list_row/user_list_row.js
@@ -63,6 +63,9 @@ export default class UserListRow extends React.PureComponent {
const teammateDisplay = displayUsername(user, teammateNameDisplay);
const showTeammateDisplay = teammateDisplay !== username;
+ const testID = this.props.testID;
+ const itemTestID = `${testID}.${id}`;
+ const displayUsernameTestID = `${testID}.display_username`;
return (
@@ -72,7 +75,7 @@ export default class UserListRow extends React.PureComponent {
enabled={enabled}
selectable={selectable}
selected={selected}
- testID={this.props.testID}
+ testID={testID}
>
-
+
{usernameDisplay}
diff --git a/app/components/custom_list/user_list_row/user_list_test.test.js b/app/components/custom_list/user_list_row/user_list_test.test.js
index 927a7eabf..594b49e65 100644
--- a/app/components/custom_list/user_list_row/user_list_test.test.js
+++ b/app/components/custom_list/user_list_row/user_list_test.test.js
@@ -29,6 +29,7 @@ describe('UserListRow', () => {
},
theme: Preferences.THEMES.default,
teammateNameDisplay: 'test',
+ testID: 'custom_list.user_item',
};
test('should match snapshot', () => {
diff --git a/app/components/sidebars/main/channels_list/channel_item/__snapshots__/channel_item.test.js.snap b/app/components/sidebars/main/channels_list/channel_item/__snapshots__/channel_item.test.js.snap
index a8fa555d2..83694769d 100644
--- a/app/components/sidebars/main/channels_list/channel_item/__snapshots__/channel_item.test.js.snap
+++ b/app/components/sidebars/main/channels_list/channel_item/__snapshots__/channel_item.test.js.snap
@@ -43,6 +43,7 @@ exports[`ChannelItem should match snapshot 1`] = `
membersCount={1}
size={16}
status="online"
+ testID="main.sidebar.channels_list.list.channel_item.channel_icon"
theme={
Object {
"awayIndicator": "#ffbc42",
@@ -158,6 +159,7 @@ exports[`ChannelItem should match snapshot for current user i.e currentUser (you
membersCount={1}
size={16}
status="online"
+ testID="main.sidebar.channels_list.list.channel_item.channel_icon"
theme={
Object {
"awayIndicator": "#ffbc42",
@@ -272,6 +274,7 @@ exports[`ChannelItem should match snapshot for current user i.e currentUser (you
membersCount={1}
size={16}
status="online"
+ testID="main.sidebar.channels_list.list.channel_item.channel_icon"
theme={
Object {
"awayIndicator": "#ffbc42",
@@ -386,6 +389,7 @@ exports[`ChannelItem should match snapshot for deactivated user and is currentCh
membersCount={1}
size={16}
status="online"
+ testID="main.sidebar.channels_list.list.channel_item.channel_icon"
theme={
Object {
"awayIndicator": "#ffbc42",
@@ -489,6 +493,7 @@ exports[`ChannelItem should match snapshot for deactivated user and is searchRes
membersCount={1}
size={16}
status="online"
+ testID="main.sidebar.channels_list.list.channel_item.channel_icon"
theme={
Object {
"awayIndicator": "#ffbc42",
@@ -593,6 +598,7 @@ exports[`ChannelItem should match snapshot for deactivated user and not searchRe
membersCount={1}
size={16}
status="online"
+ testID="main.sidebar.channels_list.list.channel_item.channel_icon"
theme={
Object {
"awayIndicator": "#ffbc42",
@@ -697,6 +703,7 @@ exports[`ChannelItem should match snapshot for isManualUnread 1`] = `
membersCount={1}
size={16}
status="online"
+ testID="main.sidebar.channels_list.list.channel_item.channel_icon"
theme={
Object {
"awayIndicator": "#ffbc42",
@@ -805,6 +812,7 @@ exports[`ChannelItem should match snapshot with draft 1`] = `
membersCount={1}
size={16}
status="online"
+ testID="main.sidebar.channels_list.list.channel_item.channel_icon"
theme={
Object {
"awayIndicator": "#ffbc42",
@@ -911,6 +919,7 @@ exports[`ChannelItem should match snapshot with mentions and muted 1`] = `
membersCount={1}
size={16}
status="online"
+ testID="main.sidebar.channels_list.list.channel_item.channel_icon"
theme={
Object {
"awayIndicator": "#ffbc42",
diff --git a/app/components/sidebars/main/channels_list/channel_item/channel_item.js b/app/components/sidebars/main/channels_list/channel_item/channel_item.js
index 369c063ae..82254c1f8 100644
--- a/app/components/sidebars/main/channels_list/channel_item/channel_item.js
+++ b/app/components/sidebars/main/channels_list/channel_item/channel_item.js
@@ -167,6 +167,7 @@ export default class ChannelItem extends PureComponent {
type={channel.type}
isArchived={isArchived}
isBot={isBot}
+ testID={`${testID}.channel_icon`}
/>
);
diff --git a/app/screens/channel_info/__snapshots__/channel_info.test.js.snap b/app/screens/channel_info/__snapshots__/channel_info.test.js.snap
index d9e8a6c2a..dbc61d281 100644
--- a/app/screens/channel_info/__snapshots__/channel_info.test.js.snap
+++ b/app/screens/channel_info/__snapshots__/channel_info.test.js.snap
@@ -40,6 +40,7 @@ exports[`channelInfo should match snapshot 1`] = `
onPermalinkPress={[Function]}
purpose="Purpose"
status="status"
+ testID="channel_info.header"
theme={
Object {
"awayIndicator": "#ffbc42",
diff --git a/app/screens/channel_info/__snapshots__/channel_info_header.test.js.snap b/app/screens/channel_info/__snapshots__/channel_info_header.test.js.snap
index e65e5574e..791ebe124 100644
--- a/app/screens/channel_info/__snapshots__/channel_info_header.test.js.snap
+++ b/app/screens/channel_info/__snapshots__/channel_info_header.test.js.snap
@@ -35,6 +35,7 @@ exports[`channel_info_header should match snapshot 1`] = `
membersCount={3}
size={24}
status="status"
+ testID="channel_info.header.channel_icon"
theme={
Object {
"awayIndicator": "#ffbc42",
@@ -77,6 +78,7 @@ exports[`channel_info_header should match snapshot 1`] = `
"fontWeight": "600",
}
}
+ testID="channel_info.header.display_name"
>
Channel name
@@ -338,6 +340,7 @@ exports[`channel_info_header should match snapshot when DM and hasGuests and is
membersCount={3}
size={24}
status="status"
+ testID="channel_info.header.channel_icon"
theme={
Object {
"awayIndicator": "#ffbc42",
@@ -380,6 +383,7 @@ exports[`channel_info_header should match snapshot when DM and hasGuests and is
"fontWeight": "600",
}
}
+ testID="channel_info.header.display_name"
>
Channel name
@@ -669,6 +673,7 @@ exports[`channel_info_header should match snapshot when DM and hasGuests but its
membersCount={3}
size={24}
status="status"
+ testID="channel_info.header.channel_icon"
theme={
Object {
"awayIndicator": "#ffbc42",
@@ -711,6 +716,7 @@ exports[`channel_info_header should match snapshot when DM and hasGuests but its
"fontWeight": "600",
}
}
+ testID="channel_info.header.display_name"
>
Channel name
@@ -972,6 +978,7 @@ exports[`channel_info_header should match snapshot when GM and hasGuests 1`] = `
membersCount={3}
size={24}
status="status"
+ testID="channel_info.header.channel_icon"
theme={
Object {
"awayIndicator": "#ffbc42",
@@ -1014,6 +1021,7 @@ exports[`channel_info_header should match snapshot when GM and hasGuests 1`] = `
"fontWeight": "600",
}
}
+ testID="channel_info.header.display_name"
>
Channel name
@@ -1303,6 +1311,7 @@ exports[`channel_info_header should match snapshot when is group constrained 1`]
membersCount={3}
size={24}
status="status"
+ testID="channel_info.header.channel_icon"
theme={
Object {
"awayIndicator": "#ffbc42",
@@ -1345,6 +1354,7 @@ exports[`channel_info_header should match snapshot when is group constrained 1`]
"fontWeight": "600",
}
}
+ testID="channel_info.header.display_name"
>
Channel name
@@ -1627,6 +1637,7 @@ exports[`channel_info_header should match snapshot when public channel and hasGu
membersCount={3}
size={24}
status="status"
+ testID="channel_info.header.channel_icon"
theme={
Object {
"awayIndicator": "#ffbc42",
@@ -1669,6 +1680,7 @@ exports[`channel_info_header should match snapshot when public channel and hasGu
"fontWeight": "600",
}
}
+ testID="channel_info.header.display_name"
>
Channel name
diff --git a/app/screens/channel_info/channel_info.js b/app/screens/channel_info/channel_info.js
index 9fcc3af88..4c7bed6b0 100644
--- a/app/screens/channel_info/channel_info.js
+++ b/app/screens/channel_info/channel_info.js
@@ -205,6 +205,7 @@ export default class ChannelInfo extends PureComponent {
isTeammateGuest={isTeammateGuest}
hasGuests={currentChannelGuestCount > 0}
isGroupConstrained={currentChannel.group_constrained}
+ testID='channel_info.header'
/>
}
diff --git a/app/screens/channel_info/channel_info_header.js b/app/screens/channel_info/channel_info_header.js
index b6689c8e3..78cccbd4c 100644
--- a/app/screens/channel_info/channel_info_header.js
+++ b/app/screens/channel_info/channel_info_header.js
@@ -42,6 +42,7 @@ export default class ChannelInfoHeader extends React.PureComponent {
isTeammateGuest: PropTypes.bool.isRequired,
hasGuests: PropTypes.bool.isRequired,
isGroupConstrained: PropTypes.bool,
+ testID: PropTypes.string,
timeZone: PropTypes.string,
};
@@ -140,6 +141,7 @@ export default class ChannelInfoHeader extends React.PureComponent {
isArchived,
isBot,
isGroupConstrained,
+ testID,
timeZone,
} = this.props;
@@ -162,11 +164,13 @@ export default class ChannelInfoHeader extends React.PureComponent {
type={type}
isArchived={isArchived}
isBot={isBot}
+ testID={`${testID}.channel_icon`}
/>
{displayName}
diff --git a/app/screens/channel_info/channel_info_header.test.js b/app/screens/channel_info/channel_info_header.test.js
index ac6731981..83f0ee8c4 100644
--- a/app/screens/channel_info/channel_info_header.test.js
+++ b/app/screens/channel_info/channel_info_header.test.js
@@ -43,6 +43,7 @@ describe('channel_info_header', () => {
isTeammateGuest: false,
hasGuests: false,
isGroupConstrained: false,
+ testID: 'channel_info.header',
};
test('should match snapshot', async () => {
diff --git a/app/screens/more_dms/more_dms.js b/app/screens/more_dms/more_dms.js
index 6ff6e30ed..134bd5a36 100644
--- a/app/screens/more_dms/more_dms.js
+++ b/app/screens/more_dms/more_dms.js
@@ -364,7 +364,7 @@ export default class MoreDirectMessages extends PureComponent {
selectable={true}
selected={selected}
enabled={true}
- testID='more_direct_messages.user'
+ testID='more_direct_messages.custom_list.user_item'
/>
);
};
@@ -516,7 +516,7 @@ export default class MoreDirectMessages extends PureComponent {
onLoadMore={this.getProfiles}
onRowPress={this.handleSelectProfile}
renderItem={this.renderItem}
- testID='more_direct_messages.list'
+ testID='more_direct_messages.custom_list'
theme={theme}
/>
diff --git a/detox/e2e/support/ui/screen/channel_info.js b/detox/e2e/support/ui/screen/channel_info.js
index d5dcb5eef..119cb5e93 100644
--- a/detox/e2e/support/ui/screen/channel_info.js
+++ b/detox/e2e/support/ui/screen/channel_info.js
@@ -10,7 +10,8 @@ class ChannelInfoScreen {
channelInfoScreen: 'channel_info.screen',
channelInfoScrollView: 'channel_info.scroll_view',
closeChannelInfoButton: 'close.channel_info.button',
- channelIconGMMemberCount: 'channel_icon.gm_member_count',
+ headerChannelIconGMMemberCount: 'channel_info.header.channel_icon.gm_member_count',
+ headerDisplayName: 'channel_info.header.display_name',
favoritePreferenceAction: 'channel_info.favorite.action',
favoriteSwitchFalse: 'channel_info.favorite.action.switch.false',
favoriteSwitchTrue: 'channel_info.favorite.action.switch.true',
@@ -33,7 +34,8 @@ class ChannelInfoScreen {
channelInfoScreen = element(by.id(this.testID.channelInfoScreen));
channelInfoScrollView = element(by.id(this.testID.channelInfoScrollView));
closeChannelInfoButton = element(by.id(this.testID.closeChannelInfoButton));
- channelIconGMMemberCount = element(by.id(this.testID.channelIconGMMemberCount));
+ headerChannelIconGMMemberCount = element(by.id(this.testID.headerChannelIconGMMemberCount));
+ headerDisplayName = element(by.id(this.testID.headerDisplayName));
favoritePreferenceAction = element(by.id(this.testID.favoritePreferenceAction));
favoriteSwitchFalse = element(by.id(this.testID.favoriteSwitchFalse));
favoriteSwitchTrue = element(by.id(this.testID.favoriteSwitchTrue));
diff --git a/detox/e2e/support/ui/screen/more_direct_messages.js b/detox/e2e/support/ui/screen/more_direct_messages.js
index 677a8e2f8..55c3d0059 100644
--- a/detox/e2e/support/ui/screen/more_direct_messages.js
+++ b/detox/e2e/support/ui/screen/more_direct_messages.js
@@ -1,21 +1,53 @@
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
// See LICENSE.txt for license information.
-import {MainSidebar} from '@support/ui/component';
+import {
+ MainSidebar,
+ SearchBar,
+} from '@support/ui/component';
class MoreDirectMessagesScreen {
testID = {
moreDirectMessagesScreen: 'more_direct_messages.screen',
+ moreDirectMessagesPrefix: 'more_direct_messages.',
startButton: 'more_direct_messages.start.button',
- list: 'more_direct_messages.list',
- user: 'more_direct_messages.user',
+ usersList: 'more_direct_messages.custom_list',
+ userItem: 'more_direct_messages.custom_list.user_item',
+ userItemDisplayUsername: 'more_direct_messages.custom_list.user_item.display_username',
}
moreDirectMessagesScreen = element(by.id(this.testID.moreDirectMessagesScreen));
startButton = element(by.id(this.testID.startButton));
+ usersList = element(by.id(this.testID.usersList));
+
+ // convenience props
+ searchBar = SearchBar.getSearchBar(this.testID.moreDirectMessagesPrefix);
+ searchInput = SearchBar.getSearchInput(this.testID.moreDirectMessagesPrefix);
+ cancelButton = SearchBar.getCancelButton(this.testID.moreDirectMessagesPrefix);
+ clearButton = SearchBar.getClearButton(this.testID.moreDirectMessagesPrefix);
+
+ getUser = (userId, diplayUsername) => {
+ const userItemTestID = `${this.testID.userItem}.${userId}`;
+ const baseMatcher = by.id(userItemTestID);
+ const userItemMatcher = diplayUsername ? baseMatcher.withDescendant(by.text(diplayUsername)) : baseMatcher;
+ const userItemUsernameDisplayMatcher = by.id(this.testID.userItemDisplayUsername).withAncestor(userItemMatcher);
+
+ return {
+ userItem: element(userItemMatcher),
+ userItemUsernameDisplay: element(userItemUsernameDisplayMatcher),
+ };
+ }
getUserAtIndex = (index) => {
- return element(by.id(this.testID.user).withAncestor(by.id(this.testID.list))).atIndex(index);
+ return element(by.id(this.testID.userItem).withAncestor(by.id(this.testID.usersList))).atIndex(index);
+ }
+
+ getUserByDisplayUsername = (displayUsername) => {
+ return element(by.text(displayUsername).withAncestor(by.id(this.testID.usersList)));
+ }
+
+ getDisplayUsernameAtIndex = (index) => {
+ return element(by.id(this.testID.userItemDisplayUsername)).atIndex(index);
}
toBeVisible = async () => {
@@ -30,6 +62,12 @@ class MoreDirectMessagesScreen {
return this.toBeVisible();
}
+
+ hasUserDisplayUsernameAtIndex = async (index, displayUsername) => {
+ await expect(
+ this.getDisplayUsernameAtIndex(index),
+ ).toHaveText(displayUsername);
+ }
}
const moreDirectMessagesScreen = new MoreDirectMessagesScreen();
diff --git a/detox/e2e/test/channel_info/header.e2e.js b/detox/e2e/test/channel_info/header.e2e.js
index 73b0b3513..d249dac7c 100644
--- a/detox/e2e/test/channel_info/header.e2e.js
+++ b/detox/e2e/test/channel_info/header.e2e.js
@@ -52,7 +52,7 @@ describe('Channel Info Header', () => {
await ChannelInfoScreen.open();
// * Verify GM member count is 3
- await expect(element(by.id(ChannelInfoScreen.testID.channelIconGMMemberCount)).atIndex(0)).toHaveText('3');
+ await expect(ChannelInfoScreen.headerChannelIconGMMemberCount).toHaveText('3');
// # Close channel info screen
await ChannelInfoScreen.close();
diff --git a/detox/e2e/test/smoke_test/group_message_channel.e2e.js b/detox/e2e/test/smoke_test/group_message_channel.e2e.js
new file mode 100644
index 000000000..74faf2521
--- /dev/null
+++ b/detox/e2e/test/smoke_test/group_message_channel.e2e.js
@@ -0,0 +1,90 @@
+// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
+// See LICENSE.txt for license information.
+
+// *******************************************************************
+// - [#] indicates a test step (e.g. # Go to a screen)
+// - [*] indicates an assertion (e.g. * Check the title)
+// - Use element testID when selecting an element. Create one if none.
+// *******************************************************************
+
+import {
+ ChannelInfoScreen,
+ ChannelScreen,
+ MoreDirectMessagesScreen,
+} from '@support/ui/screen';
+import {
+ User,
+ Setup,
+ Team,
+} from '@support/server_api';
+import {getRandomId} from '@support/utils';
+
+describe('Group Message Channel', () => {
+ const searchTerm = getRandomId();
+ let testUser;
+ let testOtherUser1;
+ let testOtherUser2;
+
+ beforeAll(async () => {
+ const {user, team} = await Setup.apiInit({userOptions: {prefix: `${searchTerm}-3-`}});
+ testUser = user;
+
+ ({user: testOtherUser1} = await User.apiCreateUser({prefix: `${searchTerm}-1-`}));
+ await Team.apiAddUserToTeam(testOtherUser1.id, team.id);
+
+ ({user: testOtherUser2} = await User.apiCreateUser({prefix: `${searchTerm}-2-`}));
+ await Team.apiAddUserToTeam(testOtherUser2.id, team.id);
+
+ // # Open channel screen
+ await ChannelScreen.open(testUser);
+ });
+
+ afterAll(async () => {
+ await ChannelScreen.logout();
+ });
+
+ it('MM-T3213 should be able to create a GM channel and post a message to the group', async () => {
+ const {
+ openMainSidebar,
+ postMessage,
+ } = ChannelScreen;
+ const {
+ getUserByDisplayUsername,
+ hasUserDisplayUsernameAtIndex,
+ getUserAtIndex,
+ searchInput,
+ startButton,
+ } = MoreDirectMessagesScreen;
+
+ // # Open more direct messages screen
+ await openMainSidebar();
+ await MoreDirectMessagesScreen.open();
+
+ // * Verify all 3 users are listed
+ await searchInput.typeText(searchTerm);
+ await hasUserDisplayUsernameAtIndex(0, `@${testOtherUser1.username}`);
+ await hasUserDisplayUsernameAtIndex(1, `@${testOtherUser2.username}`);
+ await hasUserDisplayUsernameAtIndex(2, `@${testUser.username} - you`);
+
+ // # Select profiles of other 2 users
+ await getUserAtIndex(0).tap();
+ await searchInput.typeText(searchTerm);
+ await getUserAtIndex(1).tap();
+
+ // * Verify current user is not displayed in the list
+ await expect(getUserByDisplayUsername(`@${testUser.username} - you`)).not.toBeVisible();
+
+ // # Create a GM with selected profiles
+ await startButton.tap();
+
+ // * Verify GM channel is created
+ await ChannelInfoScreen.open();
+ await expect(ChannelInfoScreen.headerDisplayName).toHaveText(`${testOtherUser1.username}, ${testOtherUser2.username}`);
+ await ChannelInfoScreen.close();
+
+ // * Verify current user can post message to GM channel
+ const message = Date.now().toString();
+ await postMessage(message);
+ await expect(element(by.text(message))).toBeVisible();
+ });
+});