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(); + }); +});