diff options
author | Mathieu Suen <mathieu.suen@sonarsource.com> | 2021-08-26 17:12:12 +0200 |
---|---|---|
committer | sonartech <sonartech@sonarsource.com> | 2021-08-30 20:08:20 +0000 |
commit | b96a80637b2f86bd8436e9aaef83b0c2c704fb31 (patch) | |
tree | 800a4405f0a563feb1015cb89954d22c185fb62a /server/sonar-web/src/main/js | |
parent | 49789f6a622f402e85a72b9b77b9dfa827785a60 (diff) | |
download | sonarqube-b96a80637b2f86bd8436e9aaef83b0c2c704fb31.tar.gz sonarqube-b96a80637b2f86bd8436e9aaef83b0c2c704fb31.zip |
SONAR-13736 Keep search query when browsing code file
Diffstat (limited to 'server/sonar-web/src/main/js')
-rw-r--r-- | server/sonar-web/src/main/js/apps/code/components/CodeApp.tsx (renamed from server/sonar-web/src/main/js/apps/code/components/AppCode.tsx) | 4 | ||||
-rw-r--r-- | server/sonar-web/src/main/js/apps/code/components/Search.tsx | 19 | ||||
-rw-r--r-- | server/sonar-web/src/main/js/apps/code/components/__tests__/CodeApp-test.tsx (renamed from server/sonar-web/src/main/js/apps/code/components/__tests__/AppCode-test.tsx) | 8 | ||||
-rw-r--r-- | server/sonar-web/src/main/js/apps/code/components/__tests__/Search-test.tsx | 111 | ||||
-rw-r--r-- | server/sonar-web/src/main/js/apps/code/components/__tests__/__snapshots__/CodeApp-test.tsx.snap (renamed from server/sonar-web/src/main/js/apps/code/components/__tests__/__snapshots__/AppCode-test.tsx.snap) | 0 | ||||
-rw-r--r-- | server/sonar-web/src/main/js/apps/code/components/__tests__/__snapshots__/Search-test.tsx.snap | 20 | ||||
-rw-r--r-- | server/sonar-web/src/main/js/apps/code/routes.ts | 2 | ||||
-rw-r--r-- | server/sonar-web/src/main/js/helpers/mocks/component.ts | 12 |
8 files changed, 162 insertions, 14 deletions
diff --git a/server/sonar-web/src/main/js/apps/code/components/AppCode.tsx b/server/sonar-web/src/main/js/apps/code/components/CodeApp.tsx index cd2924e5c31..fef599b7fee 100644 --- a/server/sonar-web/src/main/js/apps/code/components/AppCode.tsx +++ b/server/sonar-web/src/main/js/apps/code/components/CodeApp.tsx @@ -71,7 +71,7 @@ interface State { total: number; } -export class AppCode extends React.PureComponent<Props, State> { +export class CodeApp extends React.PureComponent<Props, State> { mounted = false; state: State; @@ -374,4 +374,4 @@ const mapDispatchToProps: DispatchToProps = { export default connect<StateToProps, DispatchToProps, Props>( mapStateToProps, mapDispatchToProps -)(AppCode); +)(CodeApp); diff --git a/server/sonar-web/src/main/js/apps/code/components/Search.tsx b/server/sonar-web/src/main/js/apps/code/components/Search.tsx index 00b51655ab1..a680e771c18 100644 --- a/server/sonar-web/src/main/js/apps/code/components/Search.tsx +++ b/server/sonar-web/src/main/js/apps/code/components/Search.tsx @@ -17,6 +17,7 @@ * along with this program; if not, write to the Free Software Foundation, * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ +import { omit } from 'lodash'; import * as React from 'react'; import { getTree } from '../../../api/components'; import SearchBox from '../../../components/controls/SearchBox'; @@ -32,7 +33,7 @@ interface Props { location: Location; onSearchClear: () => void; onSearchResults: (results?: T.ComponentMeasure[]) => void; - router: Pick<Router, 'push'>; + router: Router; } interface State { @@ -40,7 +41,7 @@ interface State { loading: boolean; } -class Search extends React.PureComponent<Props, State> { +export class Search extends React.PureComponent<Props, State> { mounted = false; state: State = { query: '', @@ -49,11 +50,14 @@ class Search extends React.PureComponent<Props, State> { componentDidMount() { this.mounted = true; + if (this.props.location.query.search) { + this.handleQueryChange(this.props.location.query.search); + } } - componentWillReceiveProps(nextProps: Props) { - // if the url has change, reset the current state - if (nextProps.location !== this.props.location) { + componentDidUpdate(nextProps: Props) { + // if the component has change, reset the current state + if (nextProps.location.query.id !== this.props.location.query.id) { this.setState({ query: '', loading: false @@ -79,8 +83,9 @@ class Search extends React.PureComponent<Props, State> { handleSearch = (query: string) => { if (this.mounted) { - const { branchLike, component } = this.props; + const { branchLike, component, router, location } = this.props; this.setState({ loading: true }); + router.replace({ pathname: location.pathname, query: { ...location.query, search: query } }); const isPortfolio = ['VW', 'SVW', 'APP'].includes(component.qualifier); const qualifiers = isPortfolio ? 'SVW,TRK' : 'BRC,UTS,FIL'; @@ -109,8 +114,10 @@ class Search extends React.PureComponent<Props, State> { }; handleQueryChange = (query: string) => { + const { router, location } = this.props; this.setState({ query }); if (query.length === 0) { + router.replace({ pathname: location.pathname, query: omit(location.query, 'search') }); this.props.onSearchClear(); } else { this.handleSearch(query); diff --git a/server/sonar-web/src/main/js/apps/code/components/__tests__/AppCode-test.tsx b/server/sonar-web/src/main/js/apps/code/components/__tests__/CodeApp-test.tsx index 346e5028355..20645e522f2 100644 --- a/server/sonar-web/src/main/js/apps/code/components/__tests__/AppCode-test.tsx +++ b/server/sonar-web/src/main/js/apps/code/components/__tests__/CodeApp-test.tsx @@ -25,7 +25,7 @@ import { mockIssue, mockRouter } from '../../../../helpers/testMocks'; import { waitAndUpdate } from '../../../../helpers/testUtils'; import { ComponentQualifier } from '../../../../types/component'; import { loadMoreChildren, retrieveComponent } from '../../utils'; -import { AppCode } from '../AppCode'; +import { CodeApp } from '../CodeApp'; jest.mock('../../utils', () => ({ loadMoreChildren: jest.fn().mockResolvedValue({}), @@ -174,9 +174,9 @@ it('should handle select correctly', () => { }); }); -function shallowRender(props: Partial<AppCode['props']> = {}) { - return shallow<AppCode>( - <AppCode +function shallowRender(props: Partial<CodeApp['props']> = {}) { + return shallow<CodeApp>( + <CodeApp component={{ breadcrumbs: [], name: 'foo', diff --git a/server/sonar-web/src/main/js/apps/code/components/__tests__/Search-test.tsx b/server/sonar-web/src/main/js/apps/code/components/__tests__/Search-test.tsx new file mode 100644 index 00000000000..829fb65f7a4 --- /dev/null +++ b/server/sonar-web/src/main/js/apps/code/components/__tests__/Search-test.tsx @@ -0,0 +1,111 @@ +/* + * SonarQube + * Copyright (C) 2009-2021 SonarSource SA + * mailto:info AT sonarsource DOT com + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +import { shallow } from 'enzyme'; +import * as React from 'react'; +import { getTree } from '../../../../api/components'; +import { mockComponent } from '../../../../helpers/mocks/component'; +import { mockLocation, mockRouter } from '../../../../helpers/testMocks'; +import { waitAndUpdate } from '../../../../helpers/testUtils'; +import { Search } from '../Search'; + +jest.mock('../../../../api/components', () => { + const { mockTreeComponent, mockComponent } = jest.requireActual( + '../../../../helpers/mocks/component' + ); + + return { + getTree: jest.fn().mockResolvedValue({ + baseComponent: mockTreeComponent(), + components: [mockComponent()], + paging: { pageIndex: 0, pageSize: 5, total: 20 } + }) + }; +}); + +it('should render correcly', () => { + expect(shallowRender()).toMatchSnapshot(); +}); + +it('should search correct query on mount', async () => { + const onSearchResults = jest.fn(); + const wrapper = shallowRender({ + location: mockLocation({ query: { id: 'foo', search: 'bar' } }), + onSearchResults + }); + await waitAndUpdate(wrapper); + expect(getTree).toHaveBeenCalledWith({ + component: 'my-project', + q: 'bar', + qualifiers: 'BRC,UTS,FIL', + s: 'qualifier,name' + }); + expect(onSearchResults).toHaveBeenCalledWith([ + { + breadcrumbs: [], + key: 'my-project', + name: 'MyProject', + qualifier: 'TRK', + qualityGate: { isDefault: true, key: '30', name: 'Sonar way' }, + qualityProfiles: [{ deleted: false, key: 'my-qp', language: 'ts', name: 'Sonar way' }], + tags: [] + } + ]); +}); + +it('should handle search correctly', async () => { + const router = mockRouter(); + const onSearchClear = jest.fn(); + const wrapper = shallowRender({ router, onSearchClear }); + wrapper.instance().handleQueryChange('foo'); + await waitAndUpdate(wrapper); + expect(router.replace).toHaveBeenCalledWith({ + pathname: '/path', + query: { + search: 'foo' + } + }); + expect(getTree).toHaveBeenCalledWith({ + component: 'my-project', + q: 'foo', + qualifiers: 'BRC,UTS,FIL', + s: 'qualifier,name' + }); + + wrapper.instance().handleQueryChange(''); + await waitAndUpdate(wrapper); + expect(router.replace).toHaveBeenCalledWith({ + pathname: '/path', + query: {} + }); + expect(onSearchClear).toHaveBeenCalledWith(); +}); + +function shallowRender(props?: Partial<Search['props']>) { + return shallow<Search>( + <Search + component={mockComponent()} + location={mockLocation()} + onSearchClear={jest.fn()} + onSearchResults={jest.fn()} + router={mockRouter()} + {...props} + /> + ); +} diff --git a/server/sonar-web/src/main/js/apps/code/components/__tests__/__snapshots__/AppCode-test.tsx.snap b/server/sonar-web/src/main/js/apps/code/components/__tests__/__snapshots__/CodeApp-test.tsx.snap index 233b60a1e9f..233b60a1e9f 100644 --- a/server/sonar-web/src/main/js/apps/code/components/__tests__/__snapshots__/AppCode-test.tsx.snap +++ b/server/sonar-web/src/main/js/apps/code/components/__tests__/__snapshots__/CodeApp-test.tsx.snap diff --git a/server/sonar-web/src/main/js/apps/code/components/__tests__/__snapshots__/Search-test.tsx.snap b/server/sonar-web/src/main/js/apps/code/components/__tests__/__snapshots__/Search-test.tsx.snap new file mode 100644 index 00000000000..a1d47cde013 --- /dev/null +++ b/server/sonar-web/src/main/js/apps/code/components/__tests__/__snapshots__/Search-test.tsx.snap @@ -0,0 +1,20 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`should render correcly 1`] = ` +<div + className="code-search" + id="code-search" +> + <SearchBox + minLength={3} + onChange={[Function]} + onKeyDown={[Function]} + placeholder="code.search_placeholder" + value="" + /> + <DeferredSpinner + className="spacer-left" + loading={false} + /> +</div> +`; diff --git a/server/sonar-web/src/main/js/apps/code/routes.ts b/server/sonar-web/src/main/js/apps/code/routes.ts index f35e7d14861..7a0eac5b17c 100644 --- a/server/sonar-web/src/main/js/apps/code/routes.ts +++ b/server/sonar-web/src/main/js/apps/code/routes.ts @@ -21,7 +21,7 @@ import { lazyLoadComponent } from '../../components/lazyLoadComponent'; const routes = [ { - indexRoute: { component: lazyLoadComponent(() => import('./components/AppCode')) } + indexRoute: { component: lazyLoadComponent(() => import('./components/CodeApp')) } } ]; diff --git a/server/sonar-web/src/main/js/helpers/mocks/component.ts b/server/sonar-web/src/main/js/helpers/mocks/component.ts index 990ad003a5c..19149a524c2 100644 --- a/server/sonar-web/src/main/js/helpers/mocks/component.ts +++ b/server/sonar-web/src/main/js/helpers/mocks/component.ts @@ -17,7 +17,7 @@ * along with this program; if not, write to the Free Software Foundation, * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ -import { ComponentQualifier } from '../../types/component'; +import { ComponentQualifier, TreeComponent } from '../../types/component'; import { MetricKey } from '../../types/metrics'; import { mockMeasureEnhanced } from '../testMocks'; @@ -41,6 +41,16 @@ export function mockComponent(overrides: Partial<T.Component> = {}): T.Component }; } +export function mockTreeComponent(overrides: Partial<TreeComponent>): TreeComponent { + return { + key: 'my-key', + qualifier: ComponentQualifier.Project, + name: 'component', + visibility: 'public', + ...overrides + }; +} + export function mockComponentMeasure( file = false, overrides: Partial<T.ComponentMeasure> = {} |