MM-37128 Detox/E2E Maintenance: Fix failing tests - iOS (#5544)

* MM-37128 Detox/E2E Maintenance: Fix failing tests - iOS

* Update function format
This commit is contained in:
Joseph Baylon 2021-07-15 19:05:14 -07:00 committed by GitHub
parent 8c8348ea0b
commit ecf76bbdf5
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
15 changed files with 317 additions and 109 deletions

View file

@ -128,6 +128,7 @@ const AtMentionItem = ({firstName = '', isBot, isCurrentUser, isGuest, isShared,
/>
<Text
numberOfLines={1}
testID='at_mention_item.text'
>
{Boolean(name.length) && (
<Text

View file

@ -79,7 +79,7 @@ function userProfileRow(props) {
const RowComponent = (
<View
style={style.wrapper}
testID={testID}
testID={`${testID}.row`}
>
<View style={style.container}>
{iconComponent}

View file

@ -9,8 +9,7 @@ class Autocomplete {
atMentionItemProfilePicturePrefix: 'at_mention_item.profile_picture.',
channelMentionItemPrefix: 'autocomplete.channel_mention.item.',
autocomplete: 'autocomplete',
atMentionItemName: 'at_mention_item.name',
atMentionItemUsername: 'at_mention_item.username',
atMentionItemText: 'at_mention_item.text',
atMentionSuggestionList: 'at_mention_suggestion.list',
channelMentionSuggestionList: 'channel_mention_suggestion.list',
dateSuggestion: 'autocomplete.date_suggestion',
@ -27,15 +26,13 @@ class Autocomplete {
getAtMentionItem = (userId) => {
const atMentionItemMatcher = by.id(`${this.testID.atMentionItemPrefix}${userId}`);
const atMentionItemNameMatcher = by.id(this.testID.atMentionItemName).withAncestor(atMentionItemMatcher);
const atMentionItemProfilePictureMatcher = ProfilePicture.getProfilePictureItemMatcher(this.testID.atMentionItemProfilePicturePrefix, userId).withAncestor(atMentionItemMatcher);
const atMentionItemUsernameMatcher = by.id(this.testID.atMentionItemUsername).withAncestor(atMentionItemMatcher);
const atMentionItemTextMatcher = by.id(this.testID.atMentionItemText).withAncestor(atMentionItemMatcher);
return {
atMentionItem: element(atMentionItemMatcher),
atMentionItemName: element(atMentionItemNameMatcher),
atMentionItemProfilePicture: element(atMentionItemProfilePictureMatcher),
atMentionItemUsername: element(atMentionItemUsernameMatcher),
atMentionItemText: element(atMentionItemTextMatcher),
};
}

View file

@ -0,0 +1,238 @@
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
// See LICENSE.txt for license information.
import axios from 'axios';
import jestExpect from 'expect';
import testConfig from '@support/test_config';
/**
* Get email url.
* @returns {string} email url
*/
export const getEmailUrl = () => {
const smtpUrl = testConfig.smtpUrl || 'http://localhost:10080';
return `${smtpUrl}/api/v1/mailbox`;
};
/**
* Get email reset email template.
* @param {string} userEmail - the destination user email
* @returns {string} email template
*/
export const getEmailResetEmailTemplate = (userEmail) => {
return [
'----------------------',
'You updated your email',
'----------------------',
'',
`Your email address for Mattermost has been changed to ${userEmail}.`,
'If you did not make this change, please contact the system administrator.',
'',
'To change your notification preferences, log in to your team site and go to Account Settings > Notifications.',
];
};
/**
* Get join email template.
* @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;
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> )`,
'',
'What is Mattermost?',
'Mattermost is a flexible, open source messaging platform that enables secure team collaboration.',
'Learn more ( mattermost.com )',
'',
'© 2021 Mattermost, Inc. 530 Lytton Avenue, Second floor, Palo Alto, CA, 94301',
];
};
/**
* Get mention email template.
* @param {string} sender - the email sender
* @param {string} message - the email message
* @param {string} postId - the post id where user is mentioned
* @param {string} siteName - the site name
* @param {string} teamName - the team name where user is mentioned
* @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;
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} )`,
'',
`@${sender}`,
'<skip-local-time-check>',
'',
channelDisplayName,
message,
'',
'Want to change your notifications settings?',
`Login to ${siteName} ( ${baseUrl} ) and go to Account Settings > Notifications`,
'',
'© 2021 Mattermost, Inc. 530 Lytton Avenue, Second floor, Palo Alto, CA, 94301',
];
};
/**
* Get password reset email template.
* @returns {string} email template
*/
export const getPasswordResetEmailTemplate = () => {
const baseUrl = testConfig.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> )`,
'',
'The password reset link expires in 24 hours.',
'',
'© 2021 Mattermost, Inc. 530 Lytton Avenue, Second floor, Palo Alto, CA, 94301',
];
};
/**
* Get email verify email template.
* @param {string} userEmail - the destination user email
* @returns {string} email template
*/
export const getEmailVerifyEmailTemplate = (userEmail) => {
const baseUrl = testConfig.siteUrl;
return [
'Verify your email address',
`Thanks for joining ${baseUrl.split('/')[2]}. ( ${baseUrl} )`,
'Click below to verify your email address.',
'',
`<email-verify-link-check> Verify Email ( ${baseUrl}/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.',
'',
'© 2021 Mattermost, Inc. 530 Lytton Avenue, Second floor, Palo Alto, CA, 94301',
];
};
/**
* Get welcome email template.
* @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;
return [
'Welcome to the team',
`Thanks for joining ${baseUrl.split('/')[2]}. ( ${baseUrl} )`,
'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} )`,
'',
`This email address was used to create an account with ${siteName}.`,
'If it was not you, you can safely ignore this email.',
'',
'Download the desktop and mobile apps',
'For the best experience, download the apps for PC, Mac, iOS and Android.',
'',
'Download ( https://mattermost.com/download/#mattermostApps )',
'',
'© 2021 Mattermost, Inc. 530 Lytton Avenue, Second floor, Palo Alto, CA, 94301',
];
};
/**
* Verify email body.
* @param {string} expectedBody - expected email body
* @param {*} actualBody - actual email body
*/
export const verifyEmailBody = (expectedBody, actualBody) => {
jestExpect(expectedBody.length).toEqual(actualBody.length);
for (let i = 0; i < expectedBody.length; i++) {
if (expectedBody[i].includes('skip-local-time-check')) {
continue;
}
if (expectedBody[i].includes('email-verify-link-check')) {
jestExpect(actualBody[i]).toContain('Verify Email');
jestExpect(actualBody[i]).toContain('do_verify_email?token=');
continue;
}
if (expectedBody[i].includes('join-link-check')) {
jestExpect(actualBody[i]).toContain('Join now');
jestExpect(actualBody[i]).toContain('signup_user_complete/?d=');
continue;
}
if (expectedBody[i].includes('reset-password-link-check')) {
jestExpect(actualBody[i]).toContain('Reset Password');
jestExpect(actualBody[i]).toContain('reset_password_complete?token=');
continue;
}
jestExpect(expectedBody[i]).toEqual(actualBody[i]);
}
};
/**
* Get recent email.
* @param {string} username - username of email recipient
* @param {string} mailUrl - url of email
*/
export const getRecentEmail = async (username, mailUrl = getEmailUrl()) => {
const mailboxUrl = `${mailUrl}/${username}`;
let response;
let recentEmail;
try {
response = await axios({url: mailboxUrl, method: 'get'});
recentEmail = response.data[response.data.length - 1];
} catch (error) {
return {status: error.status, data: null};
}
if (!recentEmail || !recentEmail.id) {
return {status: 501, data: null};
}
let recentEmailMessage;
const mailMessageUrl = `${mailboxUrl}/${recentEmail.id}`;
try {
response = await axios({url: mailMessageUrl, method: 'get'});
recentEmailMessage = response.data;
} catch (error) {
return {status: error.status, data: null};
}
return {status: response.status, data: recentEmailMessage};
};
/**
* Split email body text.
* @param {string} text
* @return {string} split text
*/
export const splitEmailBodyText = (text) => {
return text.split('\n').map((d) => d.trim());
};

View file

@ -1,10 +1,11 @@
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
// See LICENSE.txt for license information.
import axios from 'axios';
import {v4 as uuidv4} from 'uuid';
import testConfig from '@support/test_config';
export * from './email';
/**
* Explicit `wait` should not normally used but made available for special cases.
* @param {number} ms - duration in millisecond
@ -61,48 +62,6 @@ export const getAdminAccount = () => {
};
};
/**
* Get recent email.
* @param {string} username - username of email recipient
* @param {string} mailUrl - url of email
*/
export const getRecentEmail = async (username, mailUrl = `${testConfig.smtpUrl}/api/v1/mailbox`) => {
const mailboxUrl = `${mailUrl}/${username}`;
let response;
let recentEmail;
try {
response = await axios({url: mailboxUrl, method: 'get'});
recentEmail = response.data[response.data.length - 1];
} catch (error) {
return {status: error.status, data: null};
}
if (!recentEmail || !recentEmail.id) {
return {status: 501, data: null};
}
let recentEmailMessage;
const mailMessageUrl = `${mailboxUrl}/${recentEmail.id}`;
try {
response = await axios({url: mailMessageUrl, method: 'get'});
recentEmailMessage = response.data;
} catch (error) {
return {status: error.status, data: null};
}
return {status: response.status, data: recentEmailMessage};
};
/**
* Split email body text.
* @param {string} text
* @return {string} split text
*/
export function splitEmailBodyText(text) {
return text.split('\n').map((d) => d.trim());
}
const SECOND = 1000;
const MINUTE = 60 * 1000;

View file

@ -124,12 +124,10 @@ describe('Autocomplete', () => {
await expect(atMentionSuggestionList).toExist();
const {
atMentionItem,
atMentionItemName,
atMentionItemUsername,
atMentionItemText,
} = Autocomplete.getAtMentionItem(testUser.id);
await expect(atMentionItem).toExist();
await expect(atMentionItemName).toHaveText(`${testUser.first_name} ${testUser.last_name}`);
await expect(atMentionItemUsername).toHaveText(`(you) @${testUser.username}`);
await expect(atMentionItemText).toHaveText(`${testUser.first_name} ${testUser.last_name} (you) @${testUser.username}`);
});
it('MM-T2349 should have autocomplete using nickname', async () => {
@ -141,12 +139,10 @@ describe('Autocomplete', () => {
await expect(atMentionSuggestionList).toExist();
const {
atMentionItem,
atMentionItemName,
atMentionItemUsername,
atMentionItemText,
} = Autocomplete.getAtMentionItem(testOtherUser.id);
await expect(atMentionItem).toExist();
await expect(atMentionItemName).toHaveText(`${testOtherUser.first_name} ${testOtherUser.last_name} (${testOtherUser.nickname})`);
await expect(atMentionItemUsername).toHaveText(` @${testOtherUser.username}`);
await expect(atMentionItemText).toHaveText(`${testOtherUser.first_name} ${testOtherUser.last_name} (${testOtherUser.nickname}) @${testOtherUser.username}`);
});
it('MM-T170 should be able to search usernames as case insensitive', async () => {

View file

@ -42,20 +42,6 @@ describe('Main Sidebar', () => {
await ChannelScreen.logout();
});
it('MM-T3412 should close the sidebar menu when selecting the same channel', async () => {
// # Go to unread channel
await goToChannel(testChannel.display_name);
// # Go to the same channel again
await goToChannel(testChannel.display_name);
// * Verify sidebar menu is not open
await expect(MainSidebar.mainSidebar).not.toBeVisible();
// * Selected channel should remain the same
await expect(channelNavBarTitle).toHaveText(testChannel.display_name);
});
it('MM-T435 should not show switch teams button when jump to search is focused', async () => {
// # Open main sidebar
await openMainSidebar();
@ -70,4 +56,18 @@ describe('Main Sidebar', () => {
// # Go back to channel
await closeMainSidebar();
});
it('MM-T3412 should close the sidebar menu when selecting the same channel', async () => {
// # Go to unread channel
await goToChannel(testChannel.display_name);
// # Go to the same channel again
await goToChannel(testChannel.display_name);
// * Verify sidebar menu is not open
await expect(MainSidebar.mainSidebar).not.toBeVisible();
// * Selected channel should remain the same
await expect(channelNavBarTitle).toHaveText(testChannel.display_name);
});
});

View file

@ -56,7 +56,7 @@ describe('Channel Link', () => {
await expect(channelNavBarTitle).toHaveText(testChannel.display_name);
});
xit('MM-T178 should be able to open channel by tapping on channel link from reply thread', async () => { // related issue https://mattermost.atlassian.net/browse/MM-36532
it('MM-T178 should be able to open channel by tapping on channel link from reply thread', async () => {
// # Post a channel link
await goToChannel(townSquareChannel.display_name);
const channelLink = `${testConfig.siteUrl}/${testTeam.name}/channels/${testChannel.name}`;

View file

@ -125,8 +125,9 @@ describe('Emojis and Reactions', () => {
await AddReactionScreen.open();
// * Verify Emojis exist in recently used section
await expect(element(by.text('🦊').withAncestor(by.id('RECENTLY USED')))).toExist();
await expect(element(by.text('🐶').withAncestor(by.id('RECENTLY USED')))).toExist();
await expect(element(by.text('Recent'))).toExist();
await expect(element(by.text('🦊'))).toExist();
await expect(element(by.text('🐶'))).toExist();
// # Close AddReaction Screen
await AddReactionScreen.close();

View file

@ -9,6 +9,7 @@
import {
ChannelScreen,
PermalinkScreen,
SearchScreen,
ThreadScreen,
} from '@support/ui/screen';
@ -86,12 +87,12 @@ describe('Hashtags', () => {
await searchInput.typeText(invalid1);
await searchInput.tapReturnKey();
await element(by.text(invalid1).withAncestor(by.id(SearchScreen.testID.searchResultsList))).atIndex(1).tap();
await ThreadScreen.toBeVisible();
// # Go back to channel
await ThreadScreen.back();
await searchInput.clearText();
await SearchScreen.cancel();
// * Verify at permalink screen
await PermalinkScreen.toBeVisible();
// # Jump to recent messages
await PermalinkScreen.jumpToRecentMessages();
});
it('MM-T357 should be able to tap on hashtag from search result reply thread', async () => {
@ -106,8 +107,8 @@ describe('Hashtags', () => {
// # Open reply thread from search result and tap on hashtag
const {post} = await Post.apiGetLastPostInChannel(townSquareChannel.id);
const {searchResultPostItem} = await getSearchResultPostItem(post.id, hashtag);
await searchResultPostItem.tap();
const {searchResultPostItemHeaderReply} = await getSearchResultPostItem(post.id, hashtag);
await searchResultPostItemHeaderReply.tap();
await ThreadScreen.toBeVisible();
await element(by.text(hashtag)).atIndex(1).tap();

View file

@ -26,6 +26,7 @@ describe('Message Draft', () => {
});
afterAll(async () => {
await device.reloadReactNative();
await ChannelScreen.logout();
});

View file

@ -82,6 +82,7 @@ describe('Messaging', () => {
} = PostOptions;
// # Log in as sysadmin
await device.reloadReactNative();
await ChannelScreen.logout();
await ChannelScreen.open(getAdminAccount());

View file

@ -67,8 +67,9 @@ describe('Add Reaction', () => {
// * Verify emoji exists in recently used section
await openPostOptionsFor(post.id, message);
await AddReactionScreen.open();
await expect(element(by.text('🦊').withAncestor(by.id('RECENTLY USED')))).toExist();
await expect(element(by.text('🐶').withAncestor(by.id('RECENTLY USED')))).toExist();
await expect(element(by.text('Recent'))).toExist();
await expect(element(by.text('🦊'))).toExist();
await expect(element(by.text('🐶'))).toExist();
// # Close add reaction screen
await AddReactionScreen.close();

View file

@ -26,9 +26,11 @@ import {
} from '@support/server_api';
import {
capitalize,
getMentionEmailTemplate,
getRecentEmail,
isAndroid,
splitEmailBodyText,
verifyEmailBody,
} from '@support/utils';
describe('Email Notifications', () => {
@ -79,23 +81,20 @@ describe('Email Notifications', () => {
// # Post an at-mention message to mentioned user by other user
const testMessage = `Mention @${testUser.username} by ${testOtherUser1.username}`;
await User.apiLogin(testOtherUser1);
await Post.apiCreatePost({
const {post} = await Post.apiCreatePost({
channelId: testChannel.id,
message: testMessage,
});
// * Verify mentioned user receives email notification
const response = await getRecentEmail(testUser.username);
verifyEmailNotification(
response,
testConfig.TeamSettings.SiteName,
testTeam.display_name,
testChannel.display_name,
await verifyEmailNotification(
testConfig,
testTeam,
testChannel,
testUser,
testOtherUser1,
testMessage,
testConfig.EmailSettings.FeedbackEmail,
testConfig.SupportSettings.SupportEmail);
post.id,
testMessage);
});
it('MM-T3256_2 should be able to change email notification setting to never', async () => {
@ -192,7 +191,10 @@ async function verifyEmailNotificationsIsSetTo(sendKey) {
}
}
function verifyEmailNotification(response, siteName, teamDisplayName, channelDisplayName, mentionedUser, byUser, message, feedbackEmail, supportEmail) {
async function verifyEmailNotification(testConfig, team, channel, mentionedUser, sender, postId, message) {
const siteName = testConfig.TeamSettings.SiteName;
const feedbackEmail = testConfig.EmailSettings.FeedbackEmail;
const response = await getRecentEmail(mentionedUser.username);
const isoDate = new Date().toISOString().substring(0, 10);
const {data, status} = response;
@ -210,18 +212,17 @@ function verifyEmailNotification(response, siteName, teamDisplayName, channelDis
jestExpect(data.date).toContain(isoDate);
// * Verify that the email subject is correct
jestExpect(data.subject).toContain(`[${siteName}] Notification in ${teamDisplayName}`);
jestExpect(data.subject).toContain(`[${siteName}] Notification in ${team.display_name}`);
// * Verify that the email body is correct
const bodyText = splitEmailBodyText(data.body.text);
jestExpect(bodyText.length).toEqual(16);
jestExpect(bodyText[1]).toEqual('You have a new notification.');
jestExpect(bodyText[4]).toEqual(`Channel: ${channelDisplayName}`);
jestExpect(bodyText[5]).toContain(`@${byUser.username}`);
jestExpect(bodyText[7]).toEqual(message);
jestExpect(bodyText[9]).toContain('Go To Post');
jestExpect(bodyText[11]).toEqual(`Any questions at all, mail us any time: ${supportEmail}`);
jestExpect(bodyText[12]).toEqual('Best wishes,');
jestExpect(bodyText[13]).toEqual(`The ${siteName} Team`);
jestExpect(bodyText[15]).toEqual('To change your notification preferences, log in to your team site and go to Account Settings > Notifications.');
const expectedEmailBody = getMentionEmailTemplate(
sender.username,
message.trim(),
postId,
siteName,
team.name,
channel.display_name,
);
const actualEmailBody = splitEmailBodyText(data.body.text);
verifyEmailBody(expectedEmailBody, actualEmailBody);
}

View file

@ -10,6 +10,7 @@
import {MainSidebar} from '@support/ui/component';
import {
ChannelInfoScreen,
ChannelMembersScreen,
ChannelScreen,
MoreChannelsScreen,
} from '@support/ui/screen';
@ -29,6 +30,7 @@ describe('Public Channels', () => {
const {
archiveChannel,
leaveChannel,
manageMembersAction,
} = ChannelInfoScreen;
const {
hasChannelDisplayNameAtIndex,
@ -39,17 +41,19 @@ describe('Public Channels', () => {
getFilteredChannelByDisplayName,
searchInput,
} = MainSidebar;
let testUser;
let testPublicChannel;
let townSquareChannel;
beforeAll(async () => {
const {team, user} = await Setup.apiInit();
testUser = user;
({channel: townSquareChannel} = await Channel.apiGetChannelByName(team.id, 'town-square'));
({channel: testPublicChannel} = await Channel.apiCreateChannel({type: 'O', prefix: testPublicChannePrefix, teamId: team.id}));
// # Open channel screen
await ChannelScreen.open(user);
await ChannelScreen.open(testUser);
});
afterAll(async () => {
@ -64,7 +68,14 @@ describe('Public Channels', () => {
await getFilteredChannelByDisplayName(testPublicChannel.display_name).tap();
// * Verify user is added to the channel
await expect(element(by.text('You and @sysadmin joined the channel.'))).toBeVisible();
await ChannelInfoScreen.open();
await expect(element(by.id(ChannelInfoScreen.testID.manageMembersAction).withDescendant(by.text('2')))).toBeVisible();
await manageMembersAction.tap();
await ChannelMembersScreen.getUserByDisplayUsername(`@${testUser.username}`);
// # Go back to channel
await ChannelMembersScreen.back();
await ChannelInfoScreen.close();
});
it('MM-T3202 should be able to leave public channel', async () => {