MM-41855 Detox/E2E: Initial e2e tests in Gekidou (#6029)

* Detox/E2E: Initial e2e tests in Gekidou

* Change Local Server to Server 1

* Renamed variables for consistency

* Added team display name verification on channel list screen

* Simplified login by email setup

* Simplified smoke test server login steps
This commit is contained in:
Joseph Baylon 2022-03-12 11:44:04 -08:00 committed by GitHub
parent 5a0a2c2220
commit 35fe4081f7
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
26 changed files with 589 additions and 63 deletions

View file

@ -61,6 +61,7 @@ exports[`components/channel_list/header Channel List Header Component should mat
"lineHeight": 36,
}
}
testID="channel_list_header.team_display_name"
>
Test!
</Text>
@ -127,6 +128,7 @@ exports[`components/channel_list/header Channel List Header Component should mat
"lineHeight": 16,
}
}
testID="channel_list_header.server_display_name"
>
</Text>

View file

@ -116,7 +116,10 @@ const ChannelListHeader = ({canCreateChannels, canJoinChannels, displayName, ico
type='opacity'
>
<View style={styles.headerRow}>
<Text style={styles.headingStyles}>
<Text
style={styles.headingStyles}
testID='channel_list_header.team_display_name'
>
{displayName}
</Text>
<View style={styles.chevronButton}>
@ -139,7 +142,10 @@ const ChannelListHeader = ({canCreateChannels, canJoinChannels, displayName, ico
</TouchableWithFeedback>
</View>
}
<Text style={styles.subHeadingStyles}>
<Text
style={styles.subHeadingStyles}
testID='channel_list_header.server_display_name'
>
{serverDisplayName}
</Text>
</Animated.View>

View file

@ -106,6 +106,7 @@ type FloatingTextInputProps = TextInputProps & {
onPress?: (e: GestureResponderEvent) => void;
showErrorIcon?: boolean;
theme: Theme;
testID?: string;
value: string;
}
@ -124,6 +125,7 @@ const FloatingTextInput = forwardRef<FloatingTextInputRef, FloatingTextInputProp
value = '',
textInputStyle,
labelTextStyle,
testID,
...props
}: FloatingTextInputProps, ref) => {
const [focusedLabel, setIsFocusLabel] = useState<boolean | undefined>();
@ -255,6 +257,7 @@ const FloatingTextInput = forwardRef<FloatingTextInputRef, FloatingTextInputProp
onBlur={onTextInputBlur}
ref={inputRef}
underlineColorAndroid='transparent'
testID={testID}
/>
{Boolean(error) && (
<View style={styles.errorContainer}>
@ -264,7 +267,12 @@ const FloatingTextInput = forwardRef<FloatingTextInputRef, FloatingTextInputProp
style={styles.errorIcon}
/>
}
<Text style={styles.errorText}>{error}</Text>
<Text
style={styles.errorText}
testID={`${testID}.error`}
>
{error}
</Text>
</View>
)}
</View>

View file

@ -105,6 +105,7 @@ const AccountScreen = ({currentUser, enableCustomUserStatuses, customStatusExpir
<SafeAreaView
edges={edges}
style={styles.container}
testID='account.screen'
>
<View style={[{height: insets.top, flexDirection: 'row'}]}>
<View style={[styles.container, tabletSidebarStyle]}/>

View file

@ -83,6 +83,7 @@ const ChannelListScreen = (props: ChannelProps) => {
<SafeAreaView
style={styles.content}
edges={edges}
testID='channel_list.screen'
>
{canAddOtherServers && <Servers/>}
<Animated.View

View file

@ -71,22 +71,24 @@ export default function HomeScreen(props: HomeProps) {
>
<Tab.Screen
name={Screens.HOME}
options={{title: 'Channel', unmountOnBlur: false}}
options={{title: 'Channel', unmountOnBlur: false, tabBarTestID: 'tab_bar.home.tab'}}
>
{() => <ChannelList {...props}/>}
</Tab.Screen>
<Tab.Screen
name={Screens.SEARCH}
component={Search}
options={{unmountOnBlur: false}}
options={{unmountOnBlur: false, tabBarTestID: 'tab_bar.search.tab'}}
/>
<Tab.Screen
name={Screens.MENTIONS}
component={RecentMentions}
options={{tabBarTestID: 'tab_bar.mentions.tab'}}
/>
<Tab.Screen
name={Screens.ACCOUNT}
component={Account}
options={{tabBarTestID: 'tab_bar.account.tab'}}
/>
</Tab.Navigator>
</NavigationContainer>

View file

@ -310,12 +310,14 @@ const LoginForm = ({config, extra, keyboardAwareRef, numberSSOs, serverDisplayNa
);
}
const signinButtonTestId = buttonDisabled ? 'login_form.signin.button.disabled' : 'login_form.signin.button';
return (
<Button
testID='login.signin.button'
disabled={buttonDisabled}
onPress={onLogin}
containerStyle={[styles.loginButton, styleButtonBackground]}
testID={signinButtonTestId}
>
{buttonIcon}
<FormattedText
@ -347,7 +349,7 @@ const LoginForm = ({config, extra, keyboardAwareRef, numberSSOs, serverDisplayNa
returnKeyType='next'
showErrorIcon={false}
spellCheck={false}
testID='login.username.input'
testID='login_form.username.input'
theme={theme}
value={loginId}
/>
@ -369,7 +371,7 @@ const LoginForm = ({config, extra, keyboardAwareRef, numberSSOs, serverDisplayNa
returnKeyType='join'
spellCheck={false}
secureTextEntry={true}
testID='login.password.input'
testID='login_form.password.input'
theme={theme}
value={password}
/>
@ -378,12 +380,12 @@ const LoginForm = ({config, extra, keyboardAwareRef, numberSSOs, serverDisplayNa
<Button
onPress={onPressForgotPassword}
containerStyle={[styles.forgotPasswordBtn, error ? styles.forgotPasswordError : undefined]}
testID='login_form.forgot_password.button'
>
<FormattedText
id='login.forgot'
defaultMessage='Forgot your password?'
style={styles.forgotPasswordTxt}
testID={'login.forgot'}
/>
</Button>
)}

View file

@ -83,7 +83,7 @@ const LoginOptions = ({config, extra, hasLoginForm, launchType, launchError, lic
<FormattedText
style={styles.subheader}
id='mobile.login_options.enter_credentials'
testID='mobile.login_options.enter_credentials'
testID='login_options.description.enter_credentials'
defaultMessage='Enter your login details below.'
/>
);
@ -92,7 +92,7 @@ const LoginOptions = ({config, extra, hasLoginForm, launchType, launchError, lic
<FormattedText
style={styles.subheader}
id='mobile.login_options.select_option'
testID='mobile.login_options.select_option'
testID='login_options.description.select_option'
defaultMessage='Select a login option below.'
/>
);
@ -102,7 +102,7 @@ const LoginOptions = ({config, extra, hasLoginForm, launchType, launchError, lic
<FormattedText
style={styles.subheader}
id='mobile.login_options.none'
testID='mobile.login_options.none'
testID='login_options.description.none'
defaultMessage="You can't log in to your account yet. At least one login option must be configured. Contact your System Admin for assistance."
/>
);
@ -150,7 +150,7 @@ const LoginOptions = ({config, extra, hasLoginForm, launchType, launchError, lic
<FormattedText
defaultMessage='Log In to Your Account'
id={'mobile.login_options.heading'}
testID={'mobile.login_options.heading'}
testID={'login_options.title.login_to_account'}
style={styles.header}
/>
);
@ -159,14 +159,17 @@ const LoginOptions = ({config, extra, hasLoginForm, launchType, launchError, lic
<FormattedText
defaultMessage="Can't Log In"
id={'mobile.login_options.cant_heading'}
testID={'mobile.login_options.cant_heading'}
testID={'login_options.title.cant_login'}
style={styles.header}
/>
);
}
return (
<View style={styles.flex}>
<View
style={styles.flex}
testID='login.screen'
>
<Background theme={theme}/>
<AnimatedSafeArea style={[styles.container, transform]}>
<KeyboardAwareScrollView

View file

@ -159,6 +159,8 @@ const ServerForm = ({
);
}
const connectButtonTestId = buttonDisabled ? 'server_form.connect.button.disabled' : 'server_form.connect.button';
return (
<View style={styles.formContainer}>
<View style={[styles.fullWidth, urlError?.length ? styles.error : undefined]}>
@ -181,7 +183,7 @@ const ServerForm = ({
ref={urlRef}
returnKeyType='next'
spellCheck={false}
testID='select_server.server_url.input'
testID='server_form.server_url.input'
theme={theme}
value={url}
/>
@ -203,7 +205,7 @@ const ServerForm = ({
ref={displayNameRef}
returnKeyType='done'
spellCheck={false}
testID='select_server.server_display_name.input'
testID='server_form.server_display_name.input'
theme={theme}
value={displayName}
/>
@ -213,14 +215,14 @@ const ServerForm = ({
defaultMessage={'Choose a display name for your server'}
id={'mobile.components.select_server_view.displayHelp'}
style={styles.chooseText}
testID={'mobile.components.select_server_view.displayHelp'}
testID={'server_form.display_help'}
/>
}
<Button
containerStyle={[styles.connectButton, styleButtonBackground]}
disabled={buttonDisabled}
onPress={onConnect}
testID='select_server.connect.button'
testID={connectButtonTestId}
>
{buttonIcon}
<FormattedText

View file

@ -53,7 +53,7 @@ const ServerHeader = ({additionalServer, theme}: Props) => {
defaultMessage='Add a server'
id='servers.create_button'
style={[styles.connect, isTablet ? styles.connectTablet : undefined]}
testID='mobile.components.select_server_view.add_server'
testID='server_header.title.add_server'
/>
);
} else {
@ -62,7 +62,7 @@ const ServerHeader = ({additionalServer, theme}: Props) => {
defaultMessage='Lets Connect to a Server'
id='mobile.components.select_server_view.msg_connect'
style={[styles.connect, isTablet ? styles.connectTablet : undefined]}
testID='mobile.components.select_server_view.msg_connect'
testID='server_header.title.connect_to_server'
/>
);
}
@ -73,7 +73,7 @@ const ServerHeader = ({additionalServer, theme}: Props) => {
<FormattedText
defaultMessage='Welcome'
id='mobile.components.select_server_view.msg_welcome'
testID='mobile.components.select_server_view.msg_welcome'
testID='server_header.welcome'
style={styles.welcome}
/>
}
@ -82,7 +82,7 @@ const ServerHeader = ({additionalServer, theme}: Props) => {
defaultMessage="A Server is your team's communication hub which is accessed through a unique URL"
id='mobile.components.select_server_view.msg_description'
style={styles.description}
testID='mobile.components.select_server_view.msg_description'
testID='server_header.description'
/>
</View>
);

View file

@ -311,7 +311,10 @@ const Server = ({
}, []);
return (
<View style={styles.flex}>
<View
style={styles.flex}
testID='server.screen'
>
<Background theme={theme}/>
<AnimatedSafeArea
key={'server_content'}

View file

@ -2,15 +2,14 @@
// See LICENSE.txt for license information.
import {Plugin, System, User} from '@support/server_api';
import testConfig from '@support/test_config';
import {siteOneUrl} from '@support/test_config';
beforeAll(async () => {
// Login as sysadmin and reset server configuration
const baseUrl = testConfig.siteUrl;
await System.apiCheckSystemHealth(baseUrl);
await User.apiAdminLogin(baseUrl);
await System.apiUpdateConfig(baseUrl);
await Plugin.apiDisableNonPrepackagedPlugins(baseUrl);
await System.apiCheckSystemHealth(siteOneUrl);
await User.apiAdminLogin(siteOneUrl);
await System.apiUpdateConfig(siteOneUrl);
await Plugin.apiDisableNonPrepackagedPlugins(siteOneUrl);
await device.launchApp({
newInstance: false,

View file

@ -209,13 +209,13 @@ async function getClientLicense(baseUrl) {
return {license: out.license};
}
function getDefaultConfig() {
function getDefaultConfig(siteUrl) {
const fromEnv = {
LdapSettings: {
LdapServer: testConfig.ldapServer,
LdapPort: testConfig.ldapPort,
},
ServiceSettings: {SiteURL: testConfig.siteUrl},
ServiceSettings: {SiteURL: siteUrl},
};
return merge(defaultServerConfig, fromEnv);

View file

@ -2,12 +2,12 @@
// See LICENSE.txt for license information.
module.exports = {
serverUrl: process.env.SITE_URL || (process.env.IOS ? 'http://localhost:8065' : 'http://10.0.2.2:8065'),
siteUrl: process.env.SITE_URL || 'http://localhost:8065',
smtpUrl: process.env.SMTP_URL || 'http://localhost:9001',
serverOneUrl: process.env.SITE_1_URL || (process.env.IOS ? 'http://127.0.0.1:8065' : 'http://10.0.2.2:8065'),
siteOneUrl: process.env.SITE_1_URL || 'http://127.0.0.1:8065',
smtpUrl: process.env.SMTP_URL || 'http://127.0.0.1:9001',
adminEmail: process.env.ADMIN_EMAIL || 'sysadmin@sample.mattermost.com',
adminUsername: process.env.ADMIN_USERNAME || 'sysadmin',
adminPassword: process.env.ADMIN_PASSWORD || 'Sys@dmin-sample1',
ldapServer: process.env.LDAP_SERVER || 'localhost',
ldapServer: process.env.LDAP_SERVER || '127.0.0.1',
ldapPort: process.env.LDAP_PORT || 389,
};

View file

@ -0,0 +1,20 @@
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
// See LICENSE.txt for license information.
import {isAndroid} from '@support/utils';
class Alert {
// alert titles
logoutTitle = (serverDisplayName) => {
const title = `Are you sure you want to log out of ${serverDisplayName}?`;
return isAndroid() ? element(by.text(title)) : element(by.label(title)).atIndex(0);
};
// alert buttons
cancelButton = isAndroid() ? element(by.text('CANCEL')) : element(by.label('Cancel')).atIndex(1);
logoutButton = isAndroid() ? element(by.text('LOG OUT')) : element(by.label('Log out')).atIndex(1);
}
const alert = new Alert();
export default alert;

View file

@ -1,8 +1,8 @@
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
// See LICENSE.txt for license information.
// Imports here
import Alert from './alert';
// Exports here
export {
Alert,
};

View file

@ -0,0 +1,41 @@
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
// See LICENSE.txt for license information.
import {Alert} from '@support/ui/component';
import {HomeScreen} from '@support/ui/screen';
import {timeouts} from '@support/utils';
class AccountScreen {
testID = {
accountScreen: 'account.screen',
logoutAction: 'account.logout.action',
};
accountScreen = element(by.id(this.testID.accountScreen));
logoutAction = element(by.id(this.testID.logoutAction));
toBeVisible = async () => {
await waitFor(this.accountScreen).toBeVisible().withTimeout(timeouts.TEN_SEC);
return this.accountScreen;
};
open = async () => {
// # Open account screen
await HomeScreen.accountTab.tap();
return this.toBeVisible();
};
logout = async (serverDisplayName = null) => {
await this.logoutAction.tap();
if (serverDisplayName) {
await expect(Alert.logoutTitle(serverDisplayName)).toBeVisible();
}
await Alert.logoutButton.tap();
await expect(this.accountScreen).not.toBeVisible();
};
}
const accountScreen = new AccountScreen();
export default accountScreen;

View file

@ -0,0 +1,33 @@
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
// See LICENSE.txt for license information.
import {HomeScreen} from '@support/ui/screen';
import {timeouts} from '@support/utils';
class ChannelListScreen {
testID = {
channelListScreen: 'channel_list.screen',
headerTeamDisplayName: 'channel_list_header.team_display_name',
headerServerDisplayName: 'channel_list_header.server_display_name',
};
channelListScreen = element(by.id(this.testID.channelListScreen));
headerTeamDisplayName = element(by.id(this.testID.headerTeamDisplayName));
headerServerDisplayName = element(by.id(this.testID.headerServerDisplayName));
toBeVisible = async () => {
await waitFor(this.channelListScreen).toBeVisible().withTimeout(timeouts.TEN_SEC);
return this.channelListScreen;
};
open = async () => {
// # Open channel list screen
await HomeScreen.channelListTab.tap();
return this.toBeVisible();
};
}
const channelListScreen = new ChannelListScreen();
export default channelListScreen;

View file

@ -0,0 +1,44 @@
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
// See LICENSE.txt for license information.
import {
AccountScreen,
LoginScreen,
} from '@support/ui/screen';
import {timeouts} from '@support/utils';
class HomeScreen {
testID = {
channelListTab: 'tab_bar.channel_list.tab',
searchTab: 'tab_bar.search.tab',
mentionsTab: 'tab_bar.mentions.tab',
accountTab: 'tab_bar.account.tab',
};
channelListTab = element(by.id(this.testID.channelListTab));
searchTab = element(by.id(this.testID.searchTab));
mentionsTab = element(by.id(this.testID.mentionsTab));
accountTab = element(by.id(this.testID.accountTab));
toBeVisible = async () => {
await waitFor(this.channelListTab).toBeVisible().withTimeout(timeouts.TEN_SEC);
return this.channelListTab;
};
open = async (user = {}) => {
// # Open home screen
await LoginScreen.login(user);
return this.toBeVisible();
};
logout = async (serverDisplayName = null) => {
await AccountScreen.open();
await AccountScreen.logout(serverDisplayName);
await expect(this.channelListTab).not.toBeVisible();
};
}
const homeScreen = new HomeScreen();
export default homeScreen;

View file

@ -1,8 +1,16 @@
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
// See LICENSE.txt for license information.
// Imports here
import AccountScreen from './account';
import ChannelListScreen from './channel_list';
import HomeScreen from './home';
import LoginScreen from './login';
import ServerScreen from './server';
// Exports here
export {
AccountScreen,
ChannelListScreen,
HomeScreen,
LoginScreen,
ServerScreen,
};

View file

@ -0,0 +1,58 @@
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
// See LICENSE.txt for license information.
import {ServerScreen} from '@support/ui/screen';
import {timeouts} from '@support/utils';
class LoginScreen {
testID = {
loginScreen: 'login.screen',
backButton: 'screen.back.button',
usernameInput: 'login_form.username.input',
usernameInputError: 'login_form.username.input.error',
passwordInput: 'login_form.password.input',
passwordInputError: 'login_form.password.input.error',
forgotPasswordButton: 'login_form.forgot_password.button',
signinButton: 'login_form.signin.button',
signinButtonDisabled: 'login_form.signin.button.disabled',
};
loginScreen = element(by.id(this.testID.loginScreen));
backButton = element(by.id(this.testID.backButton));
usernameInput = element(by.id(this.testID.usernameInput));
usernameInputError = element(by.id(this.testID.usernameInputError));
passwordInput = element(by.id(this.testID.passwordInput));
passwordInputError = element(by.id(this.testID.passwordInputError));
forgotPasswordButton = element(by.id(this.testID.forgotPasswordButton));
signinButton = element(by.id(this.testID.signinButton));
signinButtonDisabled = element(by.id(this.testID.signinButtonDisabled));
toBeVisible = async () => {
await waitFor(this.loginScreen).toBeVisible().withTimeout(timeouts.TEN_SEC);
await waitFor(this.usernameInput).toBeVisible().withTimeout(timeouts.TEN_SEC);
return this.loginScreen;
};
open = async (serverUrl, serverDisplayName) => {
// # Open login screen
await ServerScreen.connectToServer(serverUrl, serverDisplayName);
return this.toBeVisible();
};
back = async () => {
await this.backButton.tap();
await expect(this.loginScreen).not.toBeVisible();
};
login = async (user = {}) => {
await this.toBeVisible();
await this.usernameInput.replaceText(user.username);
await this.passwordInput.replaceText(user.password);
await this.signinButton.tap();
};
}
const loginScreen = new LoginScreen();
export default loginScreen;

View file

@ -0,0 +1,51 @@
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
// See LICENSE.txt for license information.
import {timeouts} from '@support/utils';
class ServerScreen {
testID = {
serverScreen: 'server.screen',
headerTitleAddServer: 'server_header.title.add_server',
headerTitleConnectToServer: 'server_header.title.connect_to_server',
headerWelcome: 'server_header.welcome',
headerDescription: 'server_header.description',
serverUrlInput: 'server_form.server_url.input',
serverUrlInputError: 'server_form.server_url.input.error',
serverDisplayNameInput: 'server_form.server_display_name.input',
serverDisplayNameInputError: 'server_form.server_display_name.input.error',
displayHelp: 'server_form.display_help',
connectButton: 'server_form.connect.button',
connectButtonDisabled: 'server_form.connect.button.disabled',
};
serverScreen = element(by.id(this.testID.serverScreen));
headerTitleAddServer = element(by.id(this.testID.headerTitleAddServer));
headerTitleConnectToServer = element(by.id(this.testID.headerTitleConnectToServer));
headerWelcome = element(by.id(this.testID.headerWelcome));
headerDescription = element(by.id(this.testID.headerDescription));
serverUrlInput = element(by.id(this.testID.serverUrlInput));
serverUrlInputError = element(by.id(this.testID.serverUrlInputError));
serverDisplayNameInput = element(by.id(this.testID.serverDisplayNameInput));
serverDisplayNameInputError = element(by.id(this.testID.serverDisplayNameInputError));
displayHelp = element(by.id(this.testID.displayHelp));
connectButton = element(by.id(this.testID.connectButton));
connectButtonDisabled = element(by.id(this.testID.connectButtonDisabled));
toBeVisible = async () => {
await waitFor(this.serverScreen).toBeVisible().withTimeout(timeouts.TEN_SEC);
await waitFor(this.serverUrlInput).toBeVisible().withTimeout(timeouts.TEN_SEC);
return this.serverScreen;
};
connectToServer = async (serverUrl, serverDisplayName) => {
await this.toBeVisible();
await this.serverUrlInput.replaceText(serverUrl);
await this.serverDisplayNameInput.replaceText(serverDisplayName);
await this.connectButton.tap();
};
}
const serverScreen = new ServerScreen();
export default serverScreen;

View file

@ -35,20 +35,19 @@ export const getEmailResetEmailTemplate = (userEmail) => {
/**
* Get join email template.
* @param {string} siteUrl - the site url
* @param {string} sender - the email sender
* @param {string} userEmail - the destination user email
* @param {Object} team - the team to join
* @param {boolean} isGuest - true if guest; otherwise false
* @returns {string} email template
*/
export const getJoinEmailTemplate = (sender, userEmail, team, isGuest = false) => {
const baseUrl = testConfig.siteUrl;
export const getJoinEmailTemplate = (siteUrl, sender, userEmail, team, isGuest = false) => {
return [
`${sender} invited you to join the ${team.display_name} team.`,
`${isGuest ? 'You were invited as a guest to collaborate with the team' : 'Start collaborating with your team on Mattermost'}`,
'',
`<join-link-check> Join now ( ${baseUrl}/signup_user_complete/?d=${encodeURIComponent(JSON.stringify({display_name: team.display_name.replace(' ', '+'), email: userEmail, name: team.name}))}&t=<actual-token> )`,
`<join-link-check> Join now ( ${siteUrl}/signup_user_complete/?d=${encodeURIComponent(JSON.stringify({display_name: team.display_name.replace(' ', '+'), email: userEmail, name: team.name}))}&t=<actual-token> )`,
'',
'What is Mattermost?',
'Mattermost is a flexible, open source messaging platform that enables secure team collaboration.',
@ -60,6 +59,7 @@ export const getJoinEmailTemplate = (sender, userEmail, team, isGuest = false) =
/**
* Get mention email template.
* @param {string} siteUrl - the site url
* @param {string} sender - the email sender
* @param {string} message - the email message
* @param {string} postId - the post id where user is mentioned
@ -68,14 +68,12 @@ export const getJoinEmailTemplate = (sender, userEmail, team, isGuest = false) =
* @param {string} channelDisplayName - the channel display name where user is mentioned
* @@returns {string} email template
*/
export const getMentionEmailTemplate = (sender, message, postId, siteName, teamName, channelDisplayName) => {
const baseUrl = testConfig.siteUrl;
export const getMentionEmailTemplate = (siteUrl, sender, message, postId, siteName, teamName, channelDisplayName) => {
return [
`@${sender} mentioned you in a message`,
`While you were away, @${sender} mentioned you in the ${channelDisplayName} channel.`,
'',
`View Message ( ${baseUrl}/landing#/${teamName}/pl/${postId} )`,
`View Message ( ${siteUrl}/landing#/${teamName}/pl/${postId} )`,
'',
`@${sender}`,
'<skip-local-time-check>',
@ -84,7 +82,7 @@ export const getMentionEmailTemplate = (sender, message, postId, siteName, teamN
message,
'',
'Want to change your notifications settings?',
`Login to ${siteName} ( ${baseUrl} ) and go to Settings > Notifications`,
`Login to ${siteName} ( ${siteUrl} ) and go to Settings > Notifications`,
'',
'© 2021 Mattermost, Inc. 530 Lytton Avenue, Second floor, Palo Alto, CA, 94301',
];
@ -92,16 +90,15 @@ export const getMentionEmailTemplate = (sender, message, postId, siteName, teamN
/**
* Get password reset email template.
* @param {string} siteUrl - the site url
* @returns {string} email template
*/
export const getPasswordResetEmailTemplate = () => {
const baseUrl = testConfig.siteUrl;
export const getPasswordResetEmailTemplate = (siteUrl) => {
return [
'Reset Your Password',
'Click the button below to reset your password. If you didnt request this, you can safely ignore this email.',
'',
`<reset-password-link-check> Reset Password ( http://${baseUrl}/reset_password_complete?token=<actual-token> )`,
`<reset-password-link-check> Reset Password ( http://${siteUrl}/reset_password_complete?token=<actual-token> )`,
'',
'The password reset link expires in 24 hours.',
'',
@ -111,18 +108,17 @@ export const getPasswordResetEmailTemplate = () => {
/**
* Get email verify email template.
* @param {string} siteUrl - the site url
* @param {string} userEmail - the destination user email
* @returns {string} email template
*/
export const getEmailVerifyEmailTemplate = (userEmail) => {
const baseUrl = testConfig.siteUrl;
export const getEmailVerifyEmailTemplate = (siteUrl, userEmail) => {
return [
'Verify your email address',
`Thanks for joining ${baseUrl.split('/')[2]}. ( ${baseUrl} )`,
`Thanks for joining ${siteUrl.split('/')[2]}. ( ${siteUrl} )`,
'Click below to verify your email address.',
'',
`<email-verify-link-check> Verify Email ( ${baseUrl}/do_verify_email?token=<actual-token>&email=${encodeURIComponent(userEmail)} )`,
`<email-verify-link-check> Verify Email ( ${siteUrl}/do_verify_email?token=<actual-token>&email=${encodeURIComponent(userEmail)} )`,
'',
'This email address was used to create an account with Mattermost.',
'If it was not you, you can safely ignore this email.',
@ -133,20 +129,19 @@ export const getEmailVerifyEmailTemplate = (userEmail) => {
/**
* Get welcome email template.
* @param {string} siteUrl - the site url
* @param {string} userEmail - the destination user email
* @param {string} siteName - the site name
* @param {string} teamName - the team name where user is welcome
* @returns {string} email template
*/
export const getWelcomeEmailTemplate = (userEmail, siteName, teamName) => {
const baseUrl = testConfig.siteUrl;
export const getWelcomeEmailTemplate = (siteUrl, userEmail, siteName, teamName) => {
return [
'Welcome to the team',
`Thanks for joining ${baseUrl.split('/')[2]}. ( ${baseUrl} )`,
`Thanks for joining ${siteUrl.split('/')[2]}. ( ${siteUrl} )`,
'Click below to verify your email address.',
'',
`<email-verify-link-check> Verify Email ( ${baseUrl}/do_verify_email?token=<actual-token>&email=${encodeURIComponent(userEmail)}&redirect_to=/${teamName} )`,
`<email-verify-link-check> Verify Email ( ${siteUrl}/do_verify_email?token=<actual-token>&email=${encodeURIComponent(userEmail)}&redirect_to=/${teamName} )`,
'',
`This email address was used to create an account with ${siteName}.`,
'If it was not you, you can safely ignore this email.',

View file

@ -0,0 +1,104 @@
// 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 {serverOneUrl} from '@support/test_config';
import {
LoginScreen,
ServerScreen,
} from '@support/ui/screen';
describe('Server Login - Connect to Server', () => {
const {
connectButton,
connectButtonDisabled,
displayHelp,
headerDescription,
headerTitleConnectToServer,
headerWelcome,
serverDisplayNameInput,
serverUrlInput,
serverUrlInputError,
} = ServerScreen;
beforeEach(async () => {
// * Verify on server screen
await ServerScreen.toBeVisible();
// # Clear fields
await serverUrlInput.clearText();
await serverDisplayNameInput.clearText();
});
it('MM-T4676_1 - should match elements on server screen', async () => {
// * Verify basic elements on server screen
await expect(headerTitleConnectToServer).toBeVisible();
await expect(headerWelcome).toBeVisible();
await expect(headerDescription).toBeVisible();
await expect(serverUrlInput).toBeVisible();
await expect(serverDisplayNameInput).toBeVisible();
await expect(displayHelp).toBeVisible();
await expect(connectButtonDisabled).toBeVisible();
});
it('MM-T4676_2 - should show disabled connect button on empty server url or server display name', async () => {
// # Attempt to connect with empty server url and non-empty server display name
await serverUrlInput.replaceText('');
await serverDisplayNameInput.replaceText('Server 1');
// * Verify connect button is disabled
await expect(connectButtonDisabled).toBeVisible();
// # Attempt to connect with valid server url and empty server display name
await serverUrlInput.replaceText(serverOneUrl);
await serverDisplayNameInput.replaceText('');
// * Verify connect button is disabled
await expect(connectButtonDisabled).toBeVisible();
});
it('MM-T4676_3 - should show invalid url error on invalid server url', async () => {
// # Connect with invalid server url and non-empty server display name
const invalidServerUrl = 'invalid';
await serverUrlInput.replaceText(invalidServerUrl);
await serverDisplayNameInput.replaceText('Server 1');
await connectButton.tap();
// * Verify invalid url error
await expect(serverUrlInputError).toHaveText(`"http://${invalidServerUrl}" is not a valid API base URL`);
});
it('MM-T4676_4 - should show connection error on invalid ssl or invalid host', async () => {
// # Connect with invalid ssl and non-empty server display name
const connectionError = 'Cannot connect to the server.';
await serverUrlInput.replaceText('expired.badssl.com');
await serverDisplayNameInput.replaceText('Server 1');
await connectButton.tap();
// * Verify connection error
await expect(serverUrlInputError).toHaveText(connectionError);
// # Connect with invalid host and valid server display name
await serverUrlInput.replaceText('wrong.host.badssl.com');
await serverDisplayNameInput.replaceText('Server 1');
await connectButton.tap();
// * Verify connection error
await expect(serverUrlInputError).toHaveText(connectionError);
});
it('MM-T4676_5 - should show login screen on successful connection to server', async () => {
// # Connect to server with valid server url and non-empty server display name
await serverUrlInput.replaceText(serverOneUrl);
await serverDisplayNameInput.replaceText('Server 1');
await connectButton.tap();
// * Verify on login screen
await LoginScreen.toBeVisible();
});
});

View file

@ -0,0 +1,94 @@
// 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 {Setup} from '@support/server_api';
import {
serverOneUrl,
siteOneUrl,
} from '@support/test_config';
import {
ChannelListScreen,
LoginScreen,
ServerScreen,
} from '@support/ui/screen';
describe('Server Login - Login by Email', () => {
const {
backButton,
forgotPasswordButton,
passwordInput,
passwordInputError,
signinButton,
signinButtonDisabled,
usernameInput,
} = LoginScreen;
const serverOneDisplayName = 'Server 1';
beforeAll(async () => {
// # Connect to server
await ServerScreen.connectToServer(serverOneUrl, serverOneDisplayName);
});
beforeEach(async () => {
// * Verify on login screen
await LoginScreen.toBeVisible();
// # Clear fields
await usernameInput.clearText();
await passwordInput.clearText();
});
it('MM-T4677_1 - should match elements on login screen', async () => {
// * Verify basic elements on login screen
await expect(backButton).toBeVisible();
await expect(usernameInput).toBeVisible();
await expect(passwordInput).toBeVisible();
await expect(forgotPasswordButton).toBeVisible();
await expect(signinButtonDisabled).toBeVisible();
});
it('MM-T4677_2 - should show disabled signin button on empty username or password', async () => {
// # Log in with empty username and non-empty password
await usernameInput.replaceText('');
await passwordInput.replaceText('password');
// * Verify signin button is disabled
await expect(signinButtonDisabled).toBeVisible();
// # Log in with non-empty username and empty password
await usernameInput.replaceText('username');
await passwordInput.replaceText('');
// * Verify signin button is disabled
await expect(signinButtonDisabled).toBeVisible();
});
it('MM-T4677_3 - should show incorrect combination error on incorrect credentials', async () => {
// # Log in with incorrect credentials
await usernameInput.replaceText('username');
await passwordInput.replaceText('password');
await signinButton.tap();
// * Verify incorrect combination error
await expect(passwordInputError).toHaveText('The email and password combination is incorrect');
});
it('MM-T4677_4 - should show channel list screen on successful login', async () => {
// # Log in to server with correct credentials
const {team, user} = await Setup.apiInit(siteOneUrl);
await usernameInput.replaceText(user.username);
await passwordInput.replaceText(user.password);
await signinButton.tap();
// * Verify on channel list screen and channel list header shows team display name and server display name
await ChannelListScreen.toBeVisible();
await expect(ChannelListScreen.headerTeamDisplayName).toHaveText(team.display_name);
await expect(ChannelListScreen.headerServerDisplayName).toHaveText(serverOneDisplayName);
});
});

View file

@ -0,0 +1,49 @@
// 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 {Setup} from '@support/server_api';
import {
serverOneUrl,
siteOneUrl,
} from '@support/test_config';
import {
ChannelListScreen,
HomeScreen,
LoginScreen,
ServerScreen,
} from '@support/ui/screen';
describe('Server Login', () => {
const serverOneDisplayName = 'Server 1';
afterAll(async () => {
// # Log out
await HomeScreen.logout(serverOneDisplayName);
});
it('MM-T4675 - should be able to connect to a server, log in, and show channel list screen', async () => {
// * Verify on server screen
await ServerScreen.toBeVisible();
// # Connect to server with valid server url and non-empty server display name
await ServerScreen.connectToServer(serverOneUrl, serverOneDisplayName);
// * Verify on login screen
await LoginScreen.toBeVisible();
// # Log in to server with correct credentials
const {team, user} = await Setup.apiInit(siteOneUrl);
await LoginScreen.login(user);
// * Verify on channel list screen and channel list header shows team display name and server display name
await ChannelListScreen.toBeVisible();
await expect(ChannelListScreen.headerTeamDisplayName).toHaveText(team.display_name);
await expect(ChannelListScreen.headerServerDisplayName).toHaveText(serverOneDisplayName);
});
});