diff --git a/app/components/autocomplete/at_mention/at_mention.js b/app/components/autocomplete/at_mention/at_mention.js
index a84470f8f..7e045f59f 100644
--- a/app/components/autocomplete/at_mention/at_mention.js
+++ b/app/components/autocomplete/at_mention/at_mention.js
@@ -222,6 +222,7 @@ export default class AtMention extends PureComponent {
renderItem = ({item}) => {
return (
diff --git a/app/components/autocomplete/at_mention_item/at_mention_item.js b/app/components/autocomplete/at_mention_item/at_mention_item.js
index b745e5331..dfe3099b2 100644
--- a/app/components/autocomplete/at_mention_item/at_mention_item.js
+++ b/app/components/autocomplete/at_mention_item/at_mention_item.js
@@ -29,6 +29,7 @@ export default class AtMentionItem extends PureComponent {
isLandscape: PropTypes.bool.isRequired,
isCurrentUser: PropTypes.bool.isRequired,
showFullName: PropTypes.string,
+ testID: PropTypes.string,
};
static defaultProps = {
@@ -66,6 +67,7 @@ export default class AtMentionItem extends PureComponent {
isLandscape,
isGuest,
isCurrentUser,
+ testID,
} = this.props;
const style = getStyleFromTheme(theme);
@@ -73,6 +75,7 @@ export default class AtMentionItem extends PureComponent {
return (
{
+ const fullName = `${p.first_name.toLowerCase()} ${p.last_name.toLowerCase()}`;
return (p.delete_at === 0 && (
p.username.toLowerCase().includes(matchTerm) || p.email.toLowerCase().includes(matchTerm) ||
p.first_name.toLowerCase().includes(matchTerm) || p.last_name.toLowerCase().includes(matchTerm) ||
+ fullName.includes(matchTerm) ||
p.nickname.toLowerCase().includes(matchTerm)));
});
} else {
@@ -98,10 +100,12 @@ export const filterMembersNotInChannel = createSelector(
let profiles;
if (matchTerm) {
profiles = profilesNotInChannel.filter((p) => {
+ const fullName = `${p.first_name.toLowerCase()} ${p.last_name.toLowerCase()}`;
return (
p.username.toLowerCase().includes(matchTerm) ||
p.email.toLowerCase().includes(matchTerm) ||
p.first_name.toLowerCase().includes(matchTerm) ||
+ fullName.includes(matchTerm) ||
p.last_name.toLowerCase().includes(matchTerm) ||
p.nickname.toLowerCase().includes(matchTerm)
) && p.delete_at === 0;
diff --git a/app/selectors/autocomplete.test.js b/app/selectors/autocomplete.test.js
index 84222998c..2d6202b21 100644
--- a/app/selectors/autocomplete.test.js
+++ b/app/selectors/autocomplete.test.js
@@ -24,6 +24,7 @@ describe('Selectors.Autocomplete', () => {
['@Username', 'username'],
['@USERNAME', 'username'],
['not a match', null],
+ ['@with space', 'with space'],
];
testCases.forEach((testCase) => {
@@ -98,6 +99,9 @@ describe('Selectors.Autocomplete', () => {
profiles = filterMembersNotInChannel(state, 'example');
expect(profiles.length).toBe(2);
+
+ profiles = filterMembersNotInChannel(state, 'test u');
+ expect(profiles.length).toBe(1);
});
it('Should return profiles in channel', () => {
@@ -136,6 +140,9 @@ describe('Selectors.Autocomplete', () => {
profiles = filterMembersInChannel(state, 'example');
expect(profiles.length).toBe(2);
+
+ profiles = filterMembersInChannel(state, 'test u');
+ expect(profiles.length).toBe(1);
});
it('Should return DMs', () => {
diff --git a/detox/e2e/test/autocomplete/at_mention.e2e.js b/detox/e2e/test/autocomplete/at_mention.e2e.js
new file mode 100644
index 000000000..9f7582782
--- /dev/null
+++ b/detox/e2e/test/autocomplete/at_mention.e2e.js
@@ -0,0 +1,167 @@
+// 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 {toChannelScreen} from '@support/ui/screen';
+
+import {Setup} from '@support/server_api';
+
+describe('Autocomplete', () => {
+ let user;
+ let postInput;
+
+ beforeAll(async () => {
+ ({user} = await Setup.apiInit());
+ await toChannelScreen(user);
+ });
+
+ beforeEach(async () => {
+ await device.reloadReactNative();
+
+ // # Select post draft
+ await expect(element(by.id('channel_screen'))).toBeVisible();
+ postInput = await element(by.id('post_input'));
+ await postInput.tap();
+ });
+
+ it('MM-T3409_1 should suggest user based on username', async () => {
+ // # Type "@" to activate at mention autocomplete
+ await expect(element(by.id('autocomplete.at_mention.list'))).not.toExist();
+ await postInput.typeText('@');
+ await expect(element(by.id('autocomplete.at_mention.list'))).toExist();
+
+ // # Type username
+ await postInput.typeText(user.username);
+
+ // * Expect at mention autocomplete to contain associated user suggestion
+ await expect(element(by.id(`autocomplete.at_mention.item.${user.id}`))).toExist();
+ });
+
+ it('MM-T3409_2 should suggest user based on nickname', async () => {
+ // # Type "@" to activate at mention autocomplete
+ await expect(element(by.id('autocomplete.at_mention.list'))).not.toExist();
+ await postInput.typeText('@');
+ await expect(element(by.id('autocomplete.at_mention.list'))).toExist();
+
+ // # Type nickname
+ await postInput.typeText(user.nickname);
+
+ // * Expect at mention autocomplete to contain associated user suggestion
+ await expect(element(by.id(`autocomplete.at_mention.item.${user.id}`))).toExist();
+ });
+
+ it('MM-T3409_3 should suggest user based on first name', async () => {
+ // # Type "@" to activate at mention autocomplete
+ await expect(element(by.id('autocomplete.at_mention.list'))).not.toExist();
+ await postInput.typeText('@');
+ await expect(element(by.id('autocomplete.at_mention.list'))).toExist();
+
+ // # Type first name
+ await postInput.typeText(user.first_name);
+
+ // * Expect at mention autocomplete to contain associated user suggestion
+ await expect(element(by.id(`autocomplete.at_mention.item.${user.id}`))).toExist();
+ });
+
+ it('MM-T3409_4 should suggest user based on last name', async () => {
+ // # Type "@" to activate at mention autocomplete
+ await expect(element(by.id('autocomplete.at_mention.list'))).not.toExist();
+ await postInput.typeText('@');
+ await expect(element(by.id('autocomplete.at_mention.list'))).toExist();
+
+ // # Type last name
+ await postInput.typeText(user.last_name);
+
+ // * Expect at mention autocomplete to contain associated user suggestion
+ await expect(element(by.id(`autocomplete.at_mention.item.${user.id}`))).toExist();
+ });
+
+ it('MM-T3409_5 should suggest user based on lowercase first name', async () => {
+ // # Type "@" to activate at mention autocomplete
+ await expect(element(by.id('autocomplete.at_mention.list'))).not.toExist();
+ await postInput.typeText('@');
+ await expect(element(by.id('autocomplete.at_mention.list'))).toExist();
+
+ // # Type lowercase first name
+ await postInput.typeText(user.first_name.toLowerCase());
+
+ // * Expect at mention autocomplete to contain associated user suggestion
+ await expect(element(by.id(`autocomplete.at_mention.item.${user.id}`))).toExist();
+ });
+
+ it('MM-T3409_6 should suggest user based on lowercase last name', async () => {
+ // # Type "@" to activate at mention autocomplete
+ await expect(element(by.id('autocomplete.at_mention.list'))).not.toExist();
+ await postInput.typeText('@');
+ await expect(element(by.id('autocomplete.at_mention.list'))).toExist();
+
+ // # Type lowercase last name
+ await postInput.typeText(user.last_name.toLowerCase());
+
+ // * Expect at mention autocomplete to contain associated user suggestion
+ await expect(element(by.id(`autocomplete.at_mention.item.${user.id}`))).toExist();
+ });
+
+ it('MM-T3409_7 should suggest user based on full name with space', async () => {
+ // # Type "@" to activate at mention autocomplete
+ await expect(element(by.id('autocomplete.at_mention.list'))).not.toExist();
+ await postInput.typeText('@');
+ await expect(element(by.id('autocomplete.at_mention.list'))).toExist();
+
+ // # Type full name
+ await postInput.typeText(`${user.first_name} ${user.last_name}`);
+
+ // * Expect at mention autocomplete to contain associated user suggestion
+ await expect(element(by.id(`autocomplete.at_mention.item.${user.id}`))).toExist();
+ });
+
+ it('MM-T3409_8 should suggest user based on partial full name with space', async () => {
+ // # Type "@" to activate at mention autocomplete
+ await expect(element(by.id('autocomplete.at_mention.list'))).not.toExist();
+ await postInput.typeText('@');
+ await expect(element(by.id('autocomplete.at_mention.list'))).toExist();
+
+ // # Type partial full name
+ await postInput.typeText(`${user.first_name} ${user.last_name.substring(0, user.last_name.length - 6)}`);
+
+ // * Expect at mention autocomplete to contain associated user suggestion
+ await expect(element(by.id(`autocomplete.at_mention.item.${user.id}`))).toExist();
+ });
+
+ it('MM-T3409_9 should stop suggesting user after full name with trailing space', async () => {
+ // # Type "@" to activate at mention autocomplete
+ await expect(element(by.id('autocomplete.at_mention.list'))).not.toExist();
+ await postInput.typeText('@');
+ await expect(element(by.id('autocomplete.at_mention.list'))).toExist();
+
+ // # Type full name
+ await postInput.typeText(`${user.first_name} ${user.last_name}`);
+
+ // * Expect at mention autocomplete to contain associated user suggestion
+ await expect(element(by.id(`autocomplete.at_mention.item.${user.id}`))).toExist();
+
+ // # Type trailing space
+ await postInput.typeText(' ');
+
+ // * Expect at mention autocomplete not to contain associated user suggestion
+ await expect(element(by.id(`autocomplete.at_mention.item.${user.id}`))).not.toExist();
+ });
+
+ it('MM-T3409_10 should stop suggesting user when keyword is not associated with any user', async () => {
+ // # Type "@" to activate at mention autocomplete
+ await expect(element(by.id('autocomplete.at_mention.list'))).not.toExist();
+ await postInput.typeText('@');
+ await expect(element(by.id('autocomplete.at_mention.list'))).toExist();
+
+ // # Type keyword not associated with any user
+ await postInput.typeText(Date.now().toString());
+
+ // * Expect at mention autocomplete not to contain associated user suggestion
+ await expect(element(by.id(`autocomplete.at_mention.item.${user.id}`))).not.toExist();
+ });
+});