Skip to content

Commit

Permalink
[MM-62898] Reset filter when changing team (#8564)
Browse files Browse the repository at this point in the history
* refactor: Improve search team change handling and filter removal

* fix: revert package.resolved

* add test file

* extract regex and fix formatting of useCallback

* improve tests

* fix style
  • Loading branch information
JulienTant authored and Willyfrog committed Feb 19, 2025
1 parent eb29b5c commit c05c57f
Show file tree
Hide file tree
Showing 2 changed files with 183 additions and 2 deletions.
168 changes: 168 additions & 0 deletions app/screens/home/search/search.test.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,168 @@
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
// See LICENSE.txt for license information.

import {fireEvent, waitFor} from '@testing-library/react-native';
import React from 'react';

import {addSearchToTeamSearchHistory} from '@actions/local/team';
import {searchPosts, searchFiles} from '@actions/remote/search';
import {bottomSheet} from '@screens/navigation';
import {renderWithEverything} from '@test/intl-test-helper';
import TestHelper from '@test/test_helper';

import SearchScreen from './search';

import type {TeamModel} from '@database/models/server';
import type {Database} from '@nozbe/watermelondb';

// Some subcomponents require react-native-camera-roll, which is not available in the test environment
jest.mock('@react-native-camera-roll/camera-roll', () => ({}));

jest.mock('@react-navigation/native', () => ({
...jest.requireActual('@react-navigation/native'),
useNavigation: () => ({
getState: () => ({
index: 0,
routes: [{params: {searchTerm: ''}}],
}),
}),
useIsFocused: () => true,
}));

jest.mock('@actions/local/post', () => ({
getPosts: jest.fn().mockResolvedValue([]),
}));

jest.mock('@actions/local/team', () => ({
addSearchToTeamSearchHistory: jest.fn(),
}));

jest.mock('@actions/remote/search', () => ({
searchPosts: jest.fn().mockResolvedValue({order: [], matches: {}}),
searchFiles: jest.fn().mockResolvedValue({files: [], channels: []}),
}));

jest.mock('@mattermost/hardware-keyboard', () => ({
useHardwareKeyboardEvents: jest.fn(),
}));

jest.mock('@screens/navigation', () => ({
bottomSheet: jest.fn(),
}));

describe('SearchScreen', () => {
const baseProps = {
teamId: 'team1',
teams: [
{id: 'team1', displayName: 'Team 1'},
{id: 'team2', displayName: 'Team 2'},
] as TeamModel[],
crossTeamSearchEnabled: true,
};

let database: Database;
beforeAll(async () => {
const server = await TestHelper.setupServerDatabase();
database = server.database;
});

beforeEach(() => {
jest.clearAllMocks();
});

it('renders search screen correctly', () => {
const {getByTestId, getByText, getByPlaceholderText} = renderWithEverything(
<SearchScreen {...baseProps}/>,
{database},
);
expect(getByTestId('search_messages.screen')).toBeTruthy();

// The page title
expect(getByText('Search')).toBeTruthy();

// The search input with the expected placeholder
expect(getByPlaceholderText('Search messages & files')).toBeTruthy();
});

it('handles search input changes', () => {
const {getByTestId} = renderWithEverything(
<SearchScreen {...baseProps}/>,
{database},
);

const searchInput = getByTestId('navigation.header.search_bar.search.input');
fireEvent.changeText(searchInput, 'test search');
expect(searchInput.props.value).toBe('test search');
});

it('performs search when submitting', async () => {
const {getByTestId} = renderWithEverything(
<SearchScreen {...baseProps}/>,
{database},
);

const searchInput = getByTestId('navigation.header.search_bar.search.input');
fireEvent.changeText(searchInput, 'test search');
fireEvent(searchInput, 'submitEditing');

await waitFor(() => {
expect(searchPosts).toHaveBeenCalledWith(
expect.any(String),
'team1',
expect.objectContaining({terms: 'test search'}),
);
expect(searchFiles).toHaveBeenCalledWith(
expect.any(String),
'team1',
expect.objectContaining({terms: 'test search'}),
);
});
});

it('handles team changes', async () => {
const {getByTestId} = renderWithEverything(
<SearchScreen {...baseProps}/>,
{database},
);

const teamPicker = getByTestId('team_picker.button');
fireEvent.press(teamPicker);

expect(teamPicker).toBeTruthy();
expect(bottomSheet).toHaveBeenCalled();
});

it('clears search when clear button is pressed', async () => {
const {getByTestId} = renderWithEverything(
<SearchScreen {...baseProps}/>,
{database},
);

const searchInput = getByTestId('navigation.header.search_bar.search.input');
fireEvent.changeText(searchInput, 'test search');

const clearButton = getByTestId('navigation.header.search_bar.search.clear.button');
fireEvent.press(clearButton);

expect(searchInput.props.value).toBe('');
});

it('adds search to team history when searching in a specific team', async () => {
const {getByTestId} = renderWithEverything(
<SearchScreen {...baseProps}/>,
{database},
);

const searchInput = getByTestId('navigation.header.search_bar.search.input');
fireEvent.changeText(searchInput, 'test search');
fireEvent(searchInput, 'submitEditing');

await waitFor(() => {
expect(addSearchToTeamSearchHistory).toHaveBeenCalledWith(
expect.any(String),
'team1',
'test search',
);
});
});
});
17 changes: 15 additions & 2 deletions app/screens/home/search/search.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -79,6 +79,8 @@ const getSearchParams = (terms: string, filterValue?: FileFilter) => {

const searchScreenIndex = 1;

const CHANNEL_AND_USER_FILTERS_REGEX = /(?:from|channel|in):\s?[^\s\n]+/gi;

const SearchScreen = ({teamId, teams, crossTeamSearchEnabled}: Props) => {
const nav = useNavigation();
const isFocused = useIsFocused();
Expand Down Expand Up @@ -238,9 +240,20 @@ const SearchScreen = ({teamId, teams, crossTeamSearchEnabled}: Props) => {
setResultsLoading(false);
}, [lastSearchedValue, searchTeamId, serverUrl]);

const removeChannelAndUserFiltersFromString = (str: string) => {
return str.replace(CHANNEL_AND_USER_FILTERS_REGEX, '').trim();
};

const updateSearchTeamId = useCallback((newTeamId: string) => {
setSearchTeamId(newTeamId);
setSearchValue(removeChannelAndUserFiltersFromString(searchValue));
}, [searchValue]);

const handleResultsTeamChange = useCallback((newTeamId: string) => {
setSearchTeamId(newTeamId);
handleSearch(newTeamId, lastSearchedValue);
const cleanedSearchValue = removeChannelAndUserFiltersFromString(lastSearchedValue);
setSearchValue(cleanedSearchValue);
handleSearch(newTeamId, cleanedSearchValue);
}, [lastSearchedValue, handleSearch]);

const initialContainerStyle: AnimatedStyle<ViewStyle> = useMemo(() => {
Expand All @@ -265,7 +278,7 @@ const SearchScreen = ({teamId, teams, crossTeamSearchEnabled}: Props) => {
setRecentValue={handleRecentSearch}
searchRef={searchRef}
setSearchValue={handleModifierTextChange}
setTeamId={setSearchTeamId}
setTeamId={updateSearchTeamId}
teamId={searchTeamId}
teams={teams}
/>
Expand Down

0 comments on commit c05c57f

Please sign in to comment.