[MM-22366] Make user autocomplete also match on spaces (#4880)

* [MM-22366] Make user autocomplete also match on spaces

* Move template string to fullName variable

* Add Detox E2E tests for user autocomplete

* Add TM4J IDs

Co-authored-by: Joseph Baylon <joseph.baylon@mattermost.com>

* Add test for keyword not associated with user

Co-authored-by: Joseph Baylon <joseph.baylon@mattermost.com>
This commit is contained in:
Ed Trist 2020-10-19 14:15:18 +01:00 committed by GitHub
parent fbe2a9e575
commit cff81f168e
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
6 changed files with 184 additions and 2 deletions

View file

@ -222,6 +222,7 @@ export default class AtMention extends PureComponent {
renderItem = ({item}) => {
return (
<AtMentionItem
testID={`autocomplete.at_mention.item.${item}`}
onPress={this.completeMention}
userId={item}
/>

View file

@ -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 (
<TouchableWithFeedback
testID={testID}
key={userId}
onPress={this.completeMention}
style={padding(isLandscape)}

View file

@ -1,9 +1,9 @@
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
// See LICENSE.txt for license information.
export const AT_MENTION_REGEX = /\B(@([^@\r\n\s]*))$/i;
export const AT_MENTION_REGEX = /\B(@([^@\r\n]*))$/i;
export const AT_MENTION_REGEX_GLOBAL = /\B(@([^@\r\n\s]*))/gi;
export const AT_MENTION_REGEX_GLOBAL = /\B(@([^@\r\n]*))/gi;
export const AT_MENTION_SEARCH_REGEX = /\bfrom:\s*(\S*)$/i;

View file

@ -73,9 +73,11 @@ export const filterMembersInChannel = createSelector(
let profiles;
if (matchTerm) {
profiles = profilesInChannel.filter((p) => {
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;

View file

@ -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', () => {

View file

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