Skip to content

Commit 7cb3e12

Browse files
authored
fix: Autocomplete not hiding after using slash command (#6586)
1 parent 9172d18 commit 7cb3e12

File tree

3 files changed

+154
-16
lines changed

3 files changed

+154
-16
lines changed

app/containers/MessageComposer/MessageComposer.test.tsx

Lines changed: 138 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,9 +14,17 @@ import { colors } from '../../lib/constants';
1414
import { IRoomContext, RoomContext } from '../../views/RoomView/context';
1515
import * as EmojiKeyboardHook from './hooks/useEmojiKeyboard';
1616
import { initStore } from '../../lib/store/auxStore';
17+
import { search } from '../../lib/methods/search';
18+
import database from '../../lib/database';
19+
import { Services } from '../../lib/services';
1720

1821
jest.useFakeTimers();
1922

23+
// Ensure search returns at least one item so autocomplete renders
24+
jest.mock('../../lib/methods/search', () => ({
25+
search: jest.fn(() => [{ _id: 'u1', username: 'john', name: 'John' }])
26+
}));
27+
2028
const user = userEvent.setup();
2129

2230
const initialStoreState = () => {
@@ -128,6 +136,10 @@ let showEmojiSearchbar = false;
128136
beforeEach(() => {
129137
showEmojiKeyboard = false;
130138
showEmojiSearchbar = false;
139+
// Default DB mocks used by autocomplete
140+
(database.active.get as unknown as jest.Mock).mockImplementation(() => ({
141+
query: jest.fn(() => ({ fetch: jest.fn(() => Promise.resolve([])) }))
142+
}));
131143
jest.spyOn(EmojiKeyboardHook, 'useEmojiKeyboard').mockReturnValue({
132144
showEmojiPickerSharedValue: sharedValue,
133145
showEmojiKeyboard,
@@ -385,6 +397,132 @@ describe('MessageComposer', () => {
385397
});
386398
});
387399

400+
describe('Autocomplete', () => {
401+
test('typing @ opens autocomplete', async () => {
402+
render(<Render />);
403+
404+
await fireEvent(screen.getByTestId('message-composer-input'), 'focus');
405+
await fireEvent.changeText(screen.getByTestId('message-composer-input'), '@');
406+
await fireEvent(screen.getByTestId('message-composer-input'), 'selectionChange', {
407+
nativeEvent: { selection: { start: 1, end: 1 } }
408+
});
409+
410+
jest.advanceTimersByTime(500);
411+
412+
await waitFor(() => expect(screen.getByTestId('autocomplete')).toBeOnTheScreen());
413+
});
414+
415+
test('select @ user inserts mention and sends, autocomplete hides', async () => {
416+
const onSendMessage = jest.fn();
417+
(search as unknown as jest.Mock).mockImplementationOnce(() => [{ _id: 'u1', username: 'john', name: 'John' }]);
418+
render(<Render context={{ onSendMessage }} />);
419+
420+
await fireEvent(screen.getByTestId('message-composer-input'), 'focus');
421+
await fireEvent.changeText(screen.getByTestId('message-composer-input'), '@');
422+
await fireEvent(screen.getByTestId('message-composer-input'), 'selectionChange', {
423+
nativeEvent: { selection: { start: 1, end: 1 } }
424+
});
425+
jest.advanceTimersByTime(500);
426+
await waitFor(() => expect(screen.getByTestId('autocomplete-item-John')).toBeOnTheScreen());
427+
428+
await user.press(screen.getByTestId('autocomplete-item-John'));
429+
await waitFor(() => expect(screen.queryByTestId('autocomplete')).not.toBeOnTheScreen());
430+
431+
await user.press(screen.getByTestId('message-composer-send'));
432+
expect(onSendMessage).toHaveBeenCalledTimes(1);
433+
expect(onSendMessage).toHaveBeenCalledWith('@john', undefined);
434+
});
435+
436+
test('select # room inserts channel and sends, autocomplete hides', async () => {
437+
const onSendMessage = jest.fn();
438+
(search as unknown as jest.Mock).mockImplementationOnce(() => [{ rid: 'r1', name: 'general', t: 'c' }]);
439+
render(<Render context={{ onSendMessage }} />);
440+
441+
await fireEvent(screen.getByTestId('message-composer-input'), 'focus');
442+
await fireEvent.changeText(screen.getByTestId('message-composer-input'), '#');
443+
await fireEvent(screen.getByTestId('message-composer-input'), 'selectionChange', {
444+
nativeEvent: { selection: { start: 1, end: 1 } }
445+
});
446+
jest.advanceTimersByTime(500);
447+
await waitFor(() => expect(screen.getByTestId('autocomplete-item-general')).toBeOnTheScreen());
448+
449+
await user.press(screen.getByTestId('autocomplete-item-general'));
450+
await waitFor(() => expect(screen.queryByTestId('autocomplete')).not.toBeOnTheScreen());
451+
452+
await user.press(screen.getByTestId('message-composer-send'));
453+
expect(onSendMessage).toHaveBeenCalledTimes(1);
454+
expect(onSendMessage).toHaveBeenCalledWith('#general', undefined);
455+
});
456+
457+
test('select : emoji inserts emoji and sends, autocomplete hides', async () => {
458+
const onSendMessage = jest.fn();
459+
render(<Render context={{ onSendMessage }} />);
460+
461+
await fireEvent(screen.getByTestId('message-composer-input'), 'focus');
462+
await fireEvent.changeText(screen.getByTestId('message-composer-input'), ':smi');
463+
await fireEvent(screen.getByTestId('message-composer-input'), 'selectionChange', {
464+
nativeEvent: { selection: { start: 4, end: 4 } }
465+
});
466+
jest.advanceTimersByTime(500);
467+
await waitFor(() => expect(screen.getByTestId('autocomplete-item-smile')).toBeOnTheScreen());
468+
469+
await user.press(screen.getByTestId('autocomplete-item-smile'));
470+
await waitFor(() => expect(screen.queryByTestId('autocomplete')).not.toBeOnTheScreen());
471+
472+
await user.press(screen.getByTestId('message-composer-send'));
473+
expect(onSendMessage).toHaveBeenCalledTimes(1);
474+
expect(onSendMessage).toHaveBeenCalledWith(':smile:', undefined);
475+
});
476+
477+
test('select / command inserts command text and sends, autocomplete hides', async () => {
478+
const onSendMessage = jest.fn();
479+
const getSpy = jest.spyOn(database.active as any, 'get');
480+
(getSpy as any).mockImplementation((table: string) => {
481+
if (table === 'slash_commands') {
482+
return {
483+
query: jest.fn(() => ({ fetch: jest.fn(() => Promise.resolve([{ id: 'hello', description: 'desc' }])) }))
484+
};
485+
}
486+
return { query: jest.fn(() => ({ fetch: jest.fn(() => Promise.resolve([])) })) };
487+
});
488+
render(<Render context={{ onSendMessage }} />);
489+
490+
await fireEvent(screen.getByTestId('message-composer-input'), 'focus');
491+
await fireEvent.changeText(screen.getByTestId('message-composer-input'), '/hello');
492+
await fireEvent(screen.getByTestId('message-composer-input'), 'selectionChange', {
493+
nativeEvent: { selection: { start: 6, end: 6 } }
494+
});
495+
jest.advanceTimersByTime(500);
496+
await screen.findByTestId('autocomplete');
497+
await user.press(screen.getByTestId('message-composer-send'));
498+
await waitFor(() => expect(screen.queryByTestId('autocomplete')).not.toBeOnTheScreen());
499+
});
500+
501+
test('select ! canned response inserts text and sends, autocomplete hides', async () => {
502+
const onSendMessage = jest.fn();
503+
jest.spyOn(Services, 'getListCannedResponse').mockResolvedValueOnce({
504+
success: true,
505+
cannedResponses: [{ _id: '1', shortcut: 'brb', text: 'Be right back' }]
506+
} as any);
507+
render(<Render context={{ onSendMessage, room: { ...initialContext.room, t: 'l' } }} />);
508+
509+
await fireEvent(screen.getByTestId('message-composer-input'), 'focus');
510+
await fireEvent.changeText(screen.getByTestId('message-composer-input'), '!');
511+
await fireEvent(screen.getByTestId('message-composer-input'), 'selectionChange', {
512+
nativeEvent: { selection: { start: 1, end: 1 } }
513+
});
514+
jest.advanceTimersByTime(500);
515+
await waitFor(() => expect(screen.getByTestId('autocomplete-item-brb')).toBeOnTheScreen());
516+
517+
await user.press(screen.getByTestId('autocomplete-item-brb'));
518+
await waitFor(() => expect(screen.queryByTestId('autocomplete')).not.toBeOnTheScreen());
519+
520+
await user.press(screen.getByTestId('message-composer-send'));
521+
expect(onSendMessage).toHaveBeenCalledTimes(1);
522+
expect(onSendMessage).toHaveBeenCalledWith('Be right back', undefined);
523+
});
524+
});
525+
388526
describe('edit message', () => {
389527
const onSendMessage = jest.fn();
390528
const editCancel = jest.fn();

app/containers/MessageComposer/MessageComposer.tsx

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -81,6 +81,9 @@ export const MessageComposer = ({
8181
setAlsoSendThreadToChannel(false);
8282
}
8383

84+
// Hide autocomplete
85+
setAutocompleteParams({ text: '', type: null, params: '' });
86+
8487
if (sharing) {
8588
onSendMessage?.();
8689
return;
@@ -118,9 +121,6 @@ export const MessageComposer = ({
118121
}
119122
}
120123

121-
// Hide autocomplete
122-
setAutocompleteParams({ text: '', type: null, params: '' });
123-
124124
// Text message
125125
onSendMessage?.(textFromInput, alsoSendThreadToChannel);
126126
};

app/containers/MessageComposer/__snapshots__/MessageComposer.test.tsx.snap

Lines changed: 13 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -82,7 +82,7 @@ exports[`MessageComposer Audio tap record 1`] = `
8282
borderless={true}
8383
collapsable={false}
8484
delayLongPress={600}
85-
handlerTag={206}
85+
handlerTag={262}
8686
handlerType="NativeViewGestureHandler"
8787
hitSlop={
8888
{
@@ -177,7 +177,7 @@ exports[`MessageComposer Audio tap record 1`] = `
177177
borderless={true}
178178
collapsable={false}
179179
delayLongPress={600}
180-
handlerTag={207}
180+
handlerTag={263}
181181
handlerType="NativeViewGestureHandler"
182182
hitSlop={
183183
{
@@ -299,7 +299,7 @@ exports[`MessageComposer Quote Add quote \`abc\` 1`] = `
299299
borderless={true}
300300
collapsable={false}
301301
delayLongPress={600}
302-
handlerTag={193}
302+
handlerTag={249}
303303
handlerType="NativeViewGestureHandler"
304304
hitSlop={
305305
{
@@ -414,7 +414,7 @@ exports[`MessageComposer Quote Add quote \`abc\` 1`] = `
414414
borderless={true}
415415
collapsable={false}
416416
delayLongPress={600}
417-
handlerTag={194}
417+
handlerTag={250}
418418
handlerType="NativeViewGestureHandler"
419419
hitSlop={
420420
{
@@ -586,7 +586,7 @@ exports[`MessageComposer Quote Add quote \`abc\` 1`] = `
586586
borderless={true}
587587
collapsable={false}
588588
delayLongPress={600}
589-
handlerTag={195}
589+
handlerTag={251}
590590
handlerType="NativeViewGestureHandler"
591591
hitSlop={
592592
{
@@ -743,7 +743,7 @@ exports[`MessageComposer Quote Add quote \`def\` 1`] = `
743743
borderless={true}
744744
collapsable={false}
745745
delayLongPress={600}
746-
handlerTag={196}
746+
handlerTag={252}
747747
handlerType="NativeViewGestureHandler"
748748
hitSlop={
749749
{
@@ -858,7 +858,7 @@ exports[`MessageComposer Quote Add quote \`def\` 1`] = `
858858
borderless={true}
859859
collapsable={false}
860860
delayLongPress={600}
861-
handlerTag={197}
861+
handlerTag={253}
862862
handlerType="NativeViewGestureHandler"
863863
hitSlop={
864864
{
@@ -1031,7 +1031,7 @@ exports[`MessageComposer Quote Add quote \`def\` 1`] = `
10311031
borderless={true}
10321032
collapsable={false}
10331033
delayLongPress={600}
1034-
handlerTag={198}
1034+
handlerTag={254}
10351035
handlerType="NativeViewGestureHandler"
10361036
hitSlop={
10371037
{
@@ -1212,7 +1212,7 @@ exports[`MessageComposer Quote Add quote \`def\` 1`] = `
12121212
borderless={true}
12131213
collapsable={false}
12141214
delayLongPress={600}
1215-
handlerTag={199}
1215+
handlerTag={255}
12161216
handlerType="NativeViewGestureHandler"
12171217
hitSlop={
12181218
{
@@ -1369,7 +1369,7 @@ exports[`MessageComposer Quote Remove a quote 1`] = `
13691369
borderless={true}
13701370
collapsable={false}
13711371
delayLongPress={600}
1372-
handlerTag={200}
1372+
handlerTag={256}
13731373
handlerType="NativeViewGestureHandler"
13741374
hitSlop={
13751375
{
@@ -1484,7 +1484,7 @@ exports[`MessageComposer Quote Remove a quote 1`] = `
14841484
borderless={true}
14851485
collapsable={false}
14861486
delayLongPress={600}
1487-
handlerTag={201}
1487+
handlerTag={257}
14881488
handlerType="NativeViewGestureHandler"
14891489
hitSlop={
14901490
{
@@ -1657,7 +1657,7 @@ exports[`MessageComposer Quote Remove a quote 1`] = `
16571657
borderless={true}
16581658
collapsable={false}
16591659
delayLongPress={600}
1660-
handlerTag={202}
1660+
handlerTag={258}
16611661
handlerType="NativeViewGestureHandler"
16621662
hitSlop={
16631663
{
@@ -1838,7 +1838,7 @@ exports[`MessageComposer Quote Remove a quote 1`] = `
18381838
borderless={true}
18391839
collapsable={false}
18401840
delayLongPress={600}
1841-
handlerTag={203}
1841+
handlerTag={259}
18421842
handlerType="NativeViewGestureHandler"
18431843
hitSlop={
18441844
{

0 commit comments

Comments
 (0)