Detox/E2E: Team Sidebar e2e tests in Gekidou (#6188)

* Detox/E2E: Team Sidebar e2e tests in Gekidou

* Fixed spacing for component

* Fixed spacing for component
This commit is contained in:
Joseph Baylon
2022-04-26 08:22:33 -07:00
committed by GitHub
parent 6e2782ae1e
commit 776f56efb1
19 changed files with 153 additions and 19 deletions

View File

@@ -32,10 +32,13 @@ export default function AddTeamSlideUp({otherTeams, canCreateTeams, showTitle =
onPress={onPressCreate} onPress={onPressCreate}
showButton={canCreateTeams} showButton={canCreateTeams}
showTitle={showTitle} showTitle={showTitle}
testID='add_team_slide_up' testID='team_sidebar.add_team_slide_up'
title={intl.formatMessage({id: 'mobile.add_team.join_team', defaultMessage: 'Join Another Team'})} title={intl.formatMessage({id: 'mobile.add_team.join_team', defaultMessage: 'Join Another Team'})}
> >
<TeamList teams={otherTeams}/> <TeamList
teams={otherTeams}
testID='team_sidebar.add_team_slide_up.team_list'
/>
</BottomSheetContent> </BottomSheetContent>
); );
} }

View File

@@ -65,6 +65,7 @@ export default function AddTeam({canCreateTeams, otherTeams}: Props) {
onPress={onPress} onPress={onPress}
type='opacity' type='opacity'
style={styles.touchable} style={styles.touchable}
testID='team_sidebar.add_team.button'
> >
<CompassIcon <CompassIcon
size={28} size={28}

View File

@@ -17,6 +17,7 @@ const Empty = require('./no_teams.svg').default;
type Props = { type Props = {
teams: TeamModel[]; teams: TeamModel[];
testID?: string;
} }
const getStyleSheet = makeStyleSheetFromTheme((theme: Theme) => ({ const getStyleSheet = makeStyleSheetFromTheme((theme: Theme) => ({
@@ -58,7 +59,7 @@ const renderTeam = ({item: t}: ListRenderItemInfo<TeamModel>) => {
const keyExtractor = (item: TeamModel) => item.id; const keyExtractor = (item: TeamModel) => item.id;
export default function TeamList({teams}: Props) { export default function TeamList({teams, testID}: Props) {
const theme = useTheme(); const theme = useTheme();
const styles = getStyleSheet(theme); const styles = getStyleSheet(theme);
@@ -70,6 +71,7 @@ export default function TeamList({teams}: Props) {
renderItem={renderTeam} renderItem={renderTeam}
keyExtractor={keyExtractor} keyExtractor={keyExtractor}
contentContainerStyle={styles.contentContainer} contentContainerStyle={styles.contentContainer}
testID={`${testID}.flat_list`}
/> />
</View> </View>
); );
@@ -82,11 +84,13 @@ export default function TeamList({teams}: Props) {
id='team_list.no_other_teams.title' id='team_list.no_other_teams.title'
defaultMessage='No additional teams to join' defaultMessage='No additional teams to join'
style={styles.title} style={styles.title}
testID={`${testID}.no_other_teams.title`}
/> />
<FormattedText <FormattedText
id='team_list.no_other_teams.description' id='team_list.no_other_teams.description'
defaultMessage='To join another team, ask a Team Admin for an invitation, or create your own team.' defaultMessage='To join another team, ask a Team Admin for an invitation, or create your own team.'
style={styles.description} style={styles.description}
testID={`${testID}.no_other_teams.description`}
/> />
</View> </View>
); );

View File

@@ -55,6 +55,8 @@ export default function TeamListItem({team, currentUserId}: Props) {
dismissBottomSheet(); dismissBottomSheet();
}, []); }, []);
const teamListItemTestId = `team_sidebar.team_list.team_list_item.${team.id}`;
return ( return (
<View style={styles.container}> <View style={styles.container}>
<TouchableWithFeedback <TouchableWithFeedback
@@ -68,6 +70,7 @@ export default function TeamListItem({team, currentUserId}: Props) {
displayName={team.displayName} displayName={team.displayName}
lastIconUpdate={team.lastTeamIconUpdatedAt} lastIconUpdate={team.lastTeamIconUpdatedAt}
selected={false} selected={false}
testID={`${teamListItemTestId}.team_icon`}
/> />
</View> </View>
<Text <Text

View File

@@ -15,9 +15,10 @@ type Props = {
lastIconUpdate: number; lastIconUpdate: number;
displayName: string; displayName: string;
selected: boolean; selected: boolean;
testID?: string;
} }
export default function TeamIcon({id, lastIconUpdate, displayName, selected}: Props) { export default function TeamIcon({id, lastIconUpdate, displayName, selected, testID}: Props) {
const [imageError, setImageError] = useState(false); const [imageError, setImageError] = useState(false);
const ref = useRef<View>(null); const ref = useRef<View>(null);
const theme = useTheme(); const theme = useTheme();
@@ -41,8 +42,9 @@ export default function TeamIcon({id, lastIconUpdate, displayName, selected}: Pr
teamIconContent = ( teamIconContent = (
<Text <Text
style={styles.text} style={styles.text}
testID={`${testID}.display_name_abbreviation`}
> >
{displayName?.substr(0, 2).toUpperCase()} {displayName?.substring(0, 2).toUpperCase()}
</Text> </Text>
); );
} else { } else {
@@ -59,6 +61,7 @@ export default function TeamIcon({id, lastIconUpdate, displayName, selected}: Pr
<View <View
style={selected ? styles.containerSelected : styles.container} style={selected ? styles.containerSelected : styles.container}
ref={ref} ref={ref}
testID={testID}
> >
{teamIconContent} {teamIconContent}
</View> </View>

View File

@@ -82,18 +82,23 @@ export default function TeamItem({team, hasUnreads, mentionCount, currentTeamId}
break; break;
} }
const teamItem = `team_sidebar.team_list.team_item.${team.id}`;
const teamItemTestId = selected ? `${teamItem}.selected` : `${teamItem}.not_selected`;
return ( return (
<> <>
<View style={[styles.container, selected ? styles.containerSelected : undefined]}> <View style={[styles.container, selected ? styles.containerSelected : undefined]}>
<TouchableWithFeedback <TouchableWithFeedback
onPress={() => handleTeamChange(serverUrl, team.id)} onPress={() => handleTeamChange(serverUrl, team.id)}
type='opacity' type='opacity'
testID={teamItemTestId}
> >
<TeamIcon <TeamIcon
displayName={team.displayName} displayName={team.displayName}
id={team.id} id={team.id}
lastIconUpdate={team.lastTeamIconUpdatedAt} lastIconUpdate={team.lastTeamIconUpdatedAt}
selected={selected} selected={selected}
testID={`${teamItem}.team_icon`}
/> />
</TouchableWithFeedback> </TouchableWithFeedback>
</View> </View>

View File

@@ -10,6 +10,7 @@ import type MyTeamModel from '@typings/database/models/servers/my_team';
type Props = { type Props = {
myOrderedTeams: MyTeamModel[]; myOrderedTeams: MyTeamModel[];
testID?: string;
} }
const keyExtractor = (item: MyTeamModel) => item.id; const keyExtractor = (item: MyTeamModel) => item.id;
@@ -22,7 +23,7 @@ const renderTeam = ({item: t}: ListRenderItemInfo<MyTeamModel>) => {
); );
}; };
export default function TeamList({myOrderedTeams}: Props) { export default function TeamList({myOrderedTeams, testID}: Props) {
return ( return (
<View style={styles.container}> <View style={styles.container}>
<FlatList <FlatList
@@ -33,6 +34,7 @@ export default function TeamList({myOrderedTeams}: Props) {
keyExtractor={keyExtractor} keyExtractor={keyExtractor}
renderItem={renderTeam} renderItem={renderTeam}
showsVerticalScrollIndicator={false} showsVerticalScrollIndicator={false}
testID={`${testID}.flat_list`}
/> />
</View> </View>
); );

View File

@@ -68,7 +68,7 @@ export default function TeamSidebar({canCreateTeams, iconPad, otherTeams, teamsC
return ( return (
<Animated.View style={[styles.container, transform]}> <Animated.View style={[styles.container, transform]}>
<Animated.View style={[styles.listContainer, serverStyle]}> <Animated.View style={[styles.listContainer, serverStyle]}>
<TeamList/> <TeamList testID='team_sidebar.team_list'/>
{showAddTeam && ( {showAddTeam && (
<AddTeam <AddTeam
canCreateTeams={canCreateTeams} canCreateTeams={canCreateTeams}

View File

@@ -89,14 +89,12 @@ export default function ChannelDropdown({
channelDropdownText = intl.formatMessage({id: 'browse_channels.showArchivedChannels', defaultMessage: 'Show: Archived Channels'}); channelDropdownText = intl.formatMessage({id: 'browse_channels.showArchivedChannels', defaultMessage: 'Show: Archived Channels'});
} }
return ( return (
<View <View testID='browse_channels.channel_dropdown'>
testID='browse_channels.channel.dropdown'
>
<Text <Text
accessibilityRole={'button'} accessibilityRole={'button'}
style={style.channelDropdown} style={style.channelDropdown}
onPress={handleDropdownClick} onPress={handleDropdownClick}
testID={`browse_channels.channel.dropdown.${typeOfChannels}`} testID={`browse_channels.channel_dropdown.text.${typeOfChannels}`}
> >
{channelDropdownText} {channelDropdownText}
{' '} {' '}

View File

@@ -66,12 +66,12 @@ export default function DropdownSlideup({
<BottomSheetContent <BottomSheetContent
showButton={false} showButton={false}
showTitle={!isTablet} showTitle={!isTablet}
testID='dropdown_slideup' testID='browse_channels.dropdown_slideup'
title={intl.formatMessage({id: 'browse_channels.dropdownTitle', defaultMessage: 'Show'})} title={intl.formatMessage({id: 'browse_channels.dropdownTitle', defaultMessage: 'Show'})}
> >
<SlideUpPanelItem <SlideUpPanelItem
onPress={handlePublicPress} onPress={handlePublicPress}
testID='browse_channels.dropdownTitle.public' testID='browse_channels.dropdown_slideup_item.public_channels'
text={intl.formatMessage({id: 'browse_channels.publicChannels', defaultMessage: 'Public Channels'})} text={intl.formatMessage({id: 'browse_channels.publicChannels', defaultMessage: 'Public Channels'})}
icon={selected === PUBLIC ? 'check' : undefined} icon={selected === PUBLIC ? 'check' : undefined}
{...commonProps} {...commonProps}
@@ -79,7 +79,7 @@ export default function DropdownSlideup({
{canShowArchivedChannels && ( {canShowArchivedChannels && (
<SlideUpPanelItem <SlideUpPanelItem
onPress={handleArchivedPress} onPress={handleArchivedPress}
testID='browse_channels.dropdownTitle.public' testID='browse_channels.dropdown_slideup_item.archived_channels'
text={intl.formatMessage({id: 'browse_channels.archivedChannels', defaultMessage: 'Archived Channels'})} text={intl.formatMessage({id: 'browse_channels.archivedChannels', defaultMessage: 'Archived Channels'})}
icon={selected === ARCHIVED ? 'check' : undefined} icon={selected === ARCHIVED ? 'check' : undefined}
{...commonProps} {...commonProps}
@@ -88,7 +88,7 @@ export default function DropdownSlideup({
{sharedChannelsEnabled && ( {sharedChannelsEnabled && (
<SlideUpPanelItem <SlideUpPanelItem
onPress={handleSharedPress} onPress={handleSharedPress}
testID='browse_channels.dropdownTitle.public' testID='browse_channels.dropdown_slideup_item.shared_channels'
text={intl.formatMessage({id: 'browse_channels.sharedChannels', defaultMessage: 'Shared Channels'})} text={intl.formatMessage({id: 'browse_channels.sharedChannels', defaultMessage: 'Shared Channels'})}
icon={selected === SHARED ? 'check' : undefined} icon={selected === SHARED ? 'check' : undefined}
{...commonProps} {...commonProps}

View File

@@ -13,6 +13,7 @@ import PostDraft from './post_draft';
import PostList from './post_list'; import PostList from './post_list';
import ProfilePicture from './profile_picture'; import ProfilePicture from './profile_picture';
import SendButton from './send_button'; import SendButton from './send_button';
import TeamSidebar from './team_sidebar';
export { export {
Alert, Alert,
@@ -27,4 +28,5 @@ export {
PostList, PostList,
ProfilePicture, ProfilePicture,
SendButton, SendButton,
TeamSidebar,
}; };

View File

@@ -0,0 +1,15 @@
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
// See LICENSE.txt for license information.
class TeamSidebar {
testID = {
teamFlatList: 'team_sidebar.team_list.flat_list',
addTeamButton: 'team_sidebar.add_team.button',
};
teamFlatList = element(by.id(this.testID.teamFlatList));
addTeamButton = element(by.id(this.testID.addTeamButton));
}
const teamSidebar = new TeamSidebar();
export default teamSidebar;

View File

@@ -12,6 +12,10 @@ class BrowseChannelsScreen {
searchInput: 'browse_channels.search_bar.search.input', searchInput: 'browse_channels.search_bar.search.input',
searchClearButton: 'browse_channels.search_bar.search.clear.button', searchClearButton: 'browse_channels.search_bar.search.clear.button',
searchCancelButton: 'browse_channels.search_bar.search.cancel.button', searchCancelButton: 'browse_channels.search_bar.search.cancel.button',
channelDropdown: 'browse_channels.channel_dropdown',
channelDropdownTextPublic: 'browse_channels.channel_dropdown.text.public',
channelDropdownTextArchived: 'browse_channels.channel_dropdown.text.archived',
channelDropdownTextShared: 'browse_channels.channel_dropdown.text.shared',
flatChannelList: 'browse_channels.channel_list.flat_list', flatChannelList: 'browse_channels.channel_list.flat_list',
}; };
@@ -20,6 +24,10 @@ class BrowseChannelsScreen {
searchInput = element(by.id(this.testID.searchInput)); searchInput = element(by.id(this.testID.searchInput));
searchClearButton = element(by.id(this.testID.searchClearButton)); searchClearButton = element(by.id(this.testID.searchClearButton));
searchCancelButton = element(by.id(this.testID.searchCancelButton)); searchCancelButton = element(by.id(this.testID.searchCancelButton));
channelDropdown = element(by.id(this.testID.channelDropdown));
channelDropdownTextPublic = element(by.id(this.testID.channelDropdownTextPublic));
channelDropdownTextArchived = element(by.id(this.testID.channelDropdownTextArchived));
channelDropdownTextShared = element(by.id(this.testID.channelDropdownTextShared));
flatChannelList = element(by.id(this.testID.flatChannelList)); flatChannelList = element(by.id(this.testID.flatChannelList));
getChannelItem = (channelName: string) => { getChannelItem = (channelName: string) => {

View File

@@ -0,0 +1,41 @@
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
// See LICENSE.txt for license information.
import {BrowseChannelsScreen} from '@support/ui/screen';
import {timeouts} from '@support/utils';
import {expect} from 'detox';
class ChannelDropdownMenuScreen {
testID = {
channelDropdownMenuScreen: 'browse_channels.dropdown_slideup.screen',
publicChannelsItem: 'browse_channels.dropdown_slideup_item.public_channels',
archivedChannelsItem: 'browse_channels.dropdown_slideup_item.public_channels',
sharedChannelsItem: 'browse_channels.dropdown_slideup_item.public_channels',
};
channelDropdownMenuScreen = element(by.id(this.testID.channelDropdownMenuScreen));
publicChannelsItem = element(by.id(this.testID.publicChannelsItem));
archivedChannelsItem = element(by.id(this.testID.archivedChannelsItem));
sharedChannelsItem = element(by.id(this.testID.sharedChannelsItem));
toBeVisible = async () => {
await waitFor(this.channelDropdownMenuScreen).toExist().withTimeout(timeouts.TEN_SEC);
return this.channelDropdownMenuScreen;
};
open = async () => {
// # Open channel dropdown menu screen
await BrowseChannelsScreen.channelDropdown.tap();
return this.toBeVisible();
};
close = async () => {
await this.channelDropdownMenuScreen.tap({x: 5, y: 10});
await expect(this.channelDropdownMenuScreen).not.toBeVisible();
};
}
const channelDropdownMenuScreen = new ChannelDropdownMenuScreen();
export default channelDropdownMenuScreen;

View File

@@ -1,7 +1,10 @@
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved. // Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
// See LICENSE.txt for license information. // See LICENSE.txt for license information.
import {PlusMenu} from '@support/ui/component'; import {
PlusMenu,
TeamSidebar,
} from '@support/ui/component';
import {HomeScreen} from '@support/ui/screen'; import {HomeScreen} from '@support/ui/screen';
import {timeouts} from '@support/utils'; import {timeouts} from '@support/utils';
@@ -27,6 +30,7 @@ class ChannelListScreen {
threadsButton = element(by.id(this.testID.threadsButton)); threadsButton = element(by.id(this.testID.threadsButton));
// convenience props // convenience props
teamFlatList = TeamSidebar.teamFlatList;
browseChannelsItem = PlusMenu.browseChannelsItem; browseChannelsItem = PlusMenu.browseChannelsItem;
createNewChannelItem = PlusMenu.createNewChannelItem; createNewChannelItem = PlusMenu.createNewChannelItem;
openDirectMessageItem = PlusMenu.openDirectMessageItem; openDirectMessageItem = PlusMenu.openDirectMessageItem;
@@ -55,6 +59,18 @@ class ChannelListScreen {
return element(by.id(`category.${categoryKey}.channel_list_item.${channelName}.display_name`)); return element(by.id(`category.${categoryKey}.channel_list_item.${channelName}.display_name`));
}; };
getTeamItemSelected = (teamId: string) => {
return element(by.id(`team_sidebar.team_list.team_item.${teamId}.selected`));
};
getTeamItemNotSelected = (teamId: string) => {
return element(by.id(`team_sidebar.team_list.team_item.${teamId}.not_selected`));
};
getTeamItemDisplayNameAbbreviation = (teamId: string) => {
return element(by.id(`team_sidebar.team_list.team_item.${teamId}.team_icon.display_name_abbreviation`));
};
toBeVisible = async () => { toBeVisible = async () => {
await waitFor(this.channelListScreen).toExist().withTimeout(timeouts.TEN_SEC); await waitFor(this.channelListScreen).toExist().withTimeout(timeouts.TEN_SEC);

View File

@@ -4,6 +4,7 @@
import AccountScreen from './account'; import AccountScreen from './account';
import BrowseChannelsScreen from './browse_channels'; import BrowseChannelsScreen from './browse_channels';
import ChannelScreen from './channel'; import ChannelScreen from './channel';
import ChannelDropdownMenuScreen from './channel_dropdown_menu';
import ChannelListScreen from './channel_list'; import ChannelListScreen from './channel_list';
import CreateDirectMessageScreen from './create_direct_message'; import CreateDirectMessageScreen from './create_direct_message';
import CreateOrEditChannelScreen from './create_or_edit_channel'; import CreateOrEditChannelScreen from './create_or_edit_channel';
@@ -19,6 +20,7 @@ export {
AccountScreen, AccountScreen,
BrowseChannelsScreen, BrowseChannelsScreen,
ChannelScreen, ChannelScreen,
ChannelDropdownMenuScreen,
ChannelListScreen, ChannelListScreen,
CreateDirectMessageScreen, CreateDirectMessageScreen,
CreateOrEditChannelScreen, CreateOrEditChannelScreen,

View File

@@ -53,6 +53,7 @@ class PostOptionsScreen {
}; };
deletePost = async ({confirm = true} = {}) => { deletePost = async ({confirm = true} = {}) => {
await waitFor(this.deletePostOption).toExist().withTimeout(timeouts.TWO_SEC);
await this.deletePostOption.tap({x: 1, y: 1}); await this.deletePostOption.tap({x: 1, y: 1});
const { const {
deletePostTitle, deletePostTitle,

View File

@@ -7,7 +7,10 @@
// - Use element testID when selecting an element. Create one if none. // - Use element testID when selecting an element. Create one if none.
// ******************************************************************* // *******************************************************************
import {Setup} from '@support/server_api'; import {
Setup,
Team,
} from '@support/server_api';
import { import {
serverOneUrl, serverOneUrl,
siteOneUrl, siteOneUrl,
@@ -32,15 +35,17 @@ describe('Channels - Channel List', () => {
const townSquareChannelName = 'town-square'; const townSquareChannelName = 'town-square';
let testChannel: any; let testChannel: any;
let testTeam: any; let testTeam: any;
let testUser: any;
beforeAll(async () => { beforeAll(async () => {
const {channel, team, user} = await Setup.apiInit(siteOneUrl); const {channel, team, user} = await Setup.apiInit(siteOneUrl);
testChannel = channel; testChannel = channel;
testTeam = team; testTeam = team;
testUser = user;
// # Log in to server // # Log in to server
await ServerScreen.connectToServer(serverOneUrl, serverOneDisplayName); await ServerScreen.connectToServer(serverOneUrl, serverOneDisplayName);
await LoginScreen.login(user); await LoginScreen.login(testUser);
}); });
beforeEach(async () => { beforeEach(async () => {
@@ -162,4 +167,29 @@ describe('Channels - Channel List', () => {
xit('MM-T4728_8 - should be able to find channels', async () => { xit('MM-T4728_8 - should be able to find channels', async () => {
// NOT YET IMPLEMENTED // NOT YET IMPLEMENTED
}); });
it('MM-T4728_9 - should be able to switch between teams', async () => {
// # As admin, create a second team and add user to the second team
const {team: testTeamTwo} = await Team.apiCreateTeam(siteOneUrl, {prefix: 'a'});
await Team.apiAddUserToTeam(siteOneUrl, testUser.id, testTeamTwo.id);
// * Verify on first team and team sidebar item is selected and has correct display name abbreviation
await expect(ChannelListScreen.headerTeamDisplayName).toHaveText(testTeam.display_name);
await expect(ChannelListScreen.getTeamItemSelected(testTeam.id)).toBeVisible();
await expect(ChannelListScreen.getTeamItemDisplayNameAbbreviation(testTeam.id)).toHaveText(testTeam.display_name.substring(0, 2).toUpperCase());
// # Tap on second team item from team sidebar
await ChannelListScreen.getTeamItemNotSelected(testTeamTwo.id).tap();
// * Verify on second team and team sidebar item is selected and has correct display name abbreviation
await expect(ChannelListScreen.headerTeamDisplayName).toHaveText(testTeamTwo.display_name);
await expect(ChannelListScreen.getTeamItemSelected(testTeamTwo.id)).toBeVisible();
await expect(ChannelListScreen.getTeamItemDisplayNameAbbreviation(testTeamTwo.id)).toHaveText(testTeamTwo.display_name.substring(0, 2).toUpperCase());
// # Tap back on first team item from team sidebar
await ChannelListScreen.getTeamItemNotSelected(testTeam.id).tap();
// * Verify on first team
await expect(ChannelListScreen.headerTeamDisplayName).toHaveText(testTeam.display_name);
});
}); });

View File

@@ -116,7 +116,7 @@ describe('Channels - Create Direct Message', () => {
// * Verify no group message channel for the new users appears on channel list screen // * Verify no group message channel for the new users appears on channel list screen
const firstNewUserDisplayName = firstNewUser.username; const firstNewUserDisplayName = firstNewUser.username;
const secondNewUserDisplayName = secondNewUser.username; const secondNewUserDisplayName = secondNewUser.username;
const groupDisplayName = `${firstNewUserDisplayName}, ${secondNewUserDisplayName}, ${testUser.username}`; const groupDisplayName = `${firstNewUserDisplayName}, ${secondNewUserDisplayName}`;
await expect(element(by.text(groupDisplayName))).not.toBeVisible(); await expect(element(by.text(groupDisplayName))).not.toBeVisible();
// # Open create direct message screen, search for the first new user and tap on the first new user item // # Open create direct message screen, search for the first new user and tap on the first new user item