diff options
author | Grégoire Aubert <gregoire.aubert@sonarsource.com> | 2018-09-19 16:30:22 +0200 |
---|---|---|
committer | sonartech <sonartech@sonarsource.com> | 2018-10-10 09:23:05 +0200 |
commit | 33fb963df2b706520da3673e0ebd9947b768f681 (patch) | |
tree | 807051984b9ad0218072204585be77221a60cac4 | |
parent | 783e6e08ecc839b4a330e20852e3f7a427777832 (diff) | |
download | sonarqube-33fb963df2b706520da3673e0ebd9947b768f681.tar.gz sonarqube-33fb963df2b706520da3673e0ebd9947b768f681.zip |
SONAR-11260 Use WS api/ce/analysis_status to display warnings in dashboard
14 files changed, 154 insertions, 31 deletions
diff --git a/server/sonar-web/src/main/js/api/ce.ts b/server/sonar-web/src/main/js/api/ce.ts index 3762ec76e1c..6b6476a2798 100644 --- a/server/sonar-web/src/main/js/api/ce.ts +++ b/server/sonar-web/src/main/js/api/ce.ts @@ -21,6 +21,23 @@ import { getJSON, post, RequestData } from '../helpers/request'; import throwGlobalError from '../app/utils/throwGlobalError'; import { Task } from '../app/types'; +export function getAnalysisStatus(data: { + component: string; + branch?: string; + pullRequest?: string; +}): Promise<{ + component: { + branch?: string; + key: string; + name: string; + organization?: string; + pullRequest?: string; + warnings: string[]; + }; +}> { + return getJSON('/api/ce/analysis_status', data).catch(throwGlobalError); +} + export function getActivity(data: RequestData): Promise<{ tasks: Task[] }> { return getJSON('/api/ce/activity', data); } diff --git a/server/sonar-web/src/main/js/app/components/ComponentContainer.tsx b/server/sonar-web/src/main/js/app/components/ComponentContainer.tsx index f7c488be19a..ef37ea8376e 100644 --- a/server/sonar-web/src/main/js/app/components/ComponentContainer.tsx +++ b/server/sonar-web/src/main/js/app/components/ComponentContainer.tsx @@ -26,7 +26,7 @@ import ComponentNav from './nav/component/ComponentNav'; import { Component, BranchLike, Measure, Task } from '../types'; import handleRequiredAuthorization from '../utils/handleRequiredAuthorization'; import { getBranches, getPullRequests } from '../../api/branches'; -import { getTasksForComponent } from '../../api/ce'; +import { getTasksForComponent, getAnalysisStatus } from '../../api/ce'; import { getComponentData } from '../../api/components'; import { getMeasures } from '../../api/measures'; import { getComponentNavigation } from '../../api/nav'; @@ -58,6 +58,7 @@ interface State { isPending: boolean; loading: boolean; tasksInProgress?: Task[]; + warnings: string[]; } const FETCH_STATUS_WAIT_TIME = 3000; @@ -72,7 +73,7 @@ export class ComponentContainer extends React.PureComponent<Props, State> { constructor(props: Props) { super(props); - this.state = { branchLikes: [], isPending: false, loading: true }; + this.state = { branchLikes: [], isPending: false, loading: true, warnings: [] }; } componentDidMount() { @@ -138,6 +139,7 @@ export class ComponentContainer extends React.PureComponent<Props, State> { loading: false }); this.fetchStatus(component); + this.fetchWarnings(component, branchLike); } }) .catch(onError); @@ -254,6 +256,18 @@ export class ComponentContainer extends React.PureComponent<Props, State> { ); }; + fetchWarnings = (component: Component, branchLike?: BranchLike) => { + getAnalysisStatus({ + component: component.key, + ...getBranchLikeQuery(branchLike) + }).then( + ({ component }) => { + this.setState({ warnings: component.warnings }); + }, + () => {} + ); + }; + getCurrentBranchLike = (branchLikes: BranchLike[]) => { const { query } = this.props.location; return query.pullRequest @@ -341,6 +355,7 @@ export class ComponentContainer extends React.PureComponent<Props, State> { isInProgress={isInProgress} isPending={isPending} location={this.props.location} + warnings={this.state.warnings} /> )} {loading ? ( diff --git a/server/sonar-web/src/main/js/app/components/__tests__/ComponentContainer-test.tsx b/server/sonar-web/src/main/js/app/components/__tests__/ComponentContainer-test.tsx index 7d5ca616c2f..8e0d69dc235 100644 --- a/server/sonar-web/src/main/js/app/components/__tests__/ComponentContainer-test.tsx +++ b/server/sonar-web/src/main/js/app/components/__tests__/ComponentContainer-test.tsx @@ -43,6 +43,7 @@ jest.mock('../../../api/branches', () => ({ })); jest.mock('../../../api/ce', () => ({ + getAnalysisStatus: jest.fn().mockResolvedValue({ component: { warnings: [] } }), getTasksForComponent: jest.fn().mockResolvedValue({ queue: [] }) })); diff --git a/server/sonar-web/src/main/js/app/components/nav/component/ComponentNav.tsx b/server/sonar-web/src/main/js/app/components/nav/component/ComponentNav.tsx index 9a1332c4dcd..89561422d42 100644 --- a/server/sonar-web/src/main/js/app/components/nav/component/ComponentNav.tsx +++ b/server/sonar-web/src/main/js/app/components/nav/component/ComponentNav.tsx @@ -39,6 +39,7 @@ interface Props { isInProgress?: boolean; isPending?: boolean; location: {}; + warnings: string[]; } export default class ComponentNav extends React.PureComponent<Props> { @@ -98,7 +99,7 @@ export default class ComponentNav extends React.PureComponent<Props> { branchLike={currentBranchLike} branchMeasures={this.props.branchMeasures} component={component} - currentTask={currentTask} + warnings={this.props.warnings} /> </div> <ComponentNavMenu diff --git a/server/sonar-web/src/main/js/app/components/nav/component/ComponentNavMeta.tsx b/server/sonar-web/src/main/js/app/components/nav/component/ComponentNavMeta.tsx index f6c5487409c..217ee6d1976 100644 --- a/server/sonar-web/src/main/js/app/components/nav/component/ComponentNavMeta.tsx +++ b/server/sonar-web/src/main/js/app/components/nav/component/ComponentNavMeta.tsx @@ -27,8 +27,7 @@ import { isLoggedIn, HomePageType, HomePage, - Measure, - Task + Measure } from '../../../types'; import BranchMeasures from '../../../../components/common/BranchMeasures'; import BranchStatus from '../../../../components/common/BranchStatus'; @@ -54,10 +53,16 @@ interface Props extends StateProps { branchLike?: BranchLike; branchMeasures?: Measure[]; component: Component; - currentTask?: Task; + warnings: string[]; } -export function ComponentNavMeta({ branchLike, branchMeasures, component, currentTask, currentUser }: Props) { +export function ComponentNavMeta({ + branchLike, + branchMeasures, + component, + currentUser, + warnings +}: Props) { const mainBranch = !branchLike || isMainBranch(branchLike); const longBranch = isLongLivingBranch(branchLike); const currentPage = getCurrentPage(component, branchLike); @@ -65,8 +70,7 @@ export function ComponentNavMeta({ branchLike, branchMeasures, component, curren return ( <div className="navbar-context-meta"> - {currentTask && - Boolean(currentTask.warningCount) && <ComponentNavWarnings task={currentTask} />} + {warnings.length > 0 && <ComponentNavWarnings warnings={warnings} />} {component.analysisDate && ( <div className="spacer-left text-ellipsis"> <DateTimeFormatter date={component.analysisDate} /> diff --git a/server/sonar-web/src/main/js/app/components/nav/component/ComponentNavWarnings.tsx b/server/sonar-web/src/main/js/app/components/nav/component/ComponentNavWarnings.tsx index 192d562afd7..a18d094080c 100644 --- a/server/sonar-web/src/main/js/app/components/nav/component/ComponentNavWarnings.tsx +++ b/server/sonar-web/src/main/js/app/components/nav/component/ComponentNavWarnings.tsx @@ -21,7 +21,6 @@ import * as React from 'react'; import { FormattedMessage } from 'react-intl'; import { lazyLoad } from '../../../../components/lazyLoad'; import WarningIcon from '../../../../components/icons-components/WarningIcon'; -import { Task } from '../../../types'; import { translate, translateWithParameters } from '../../../../helpers/l10n'; const AnalysisWarningsModal = lazyLoad(() => @@ -29,7 +28,7 @@ const AnalysisWarningsModal = lazyLoad(() => ); interface Props { - task: Pick<Task, 'id' | 'warningCount'>; + warnings: string[]; } interface State { @@ -62,7 +61,7 @@ export default class ComponentNavWarnings extends React.PureComponent<Props, Sta <a href="#" onClick={this.handleClick}> {translateWithParameters( 'component_navigation.x_warnings', - String(this.props.task.warningCount) + String(this.props.warnings.length) )} </a> ) @@ -70,7 +69,7 @@ export default class ComponentNavWarnings extends React.PureComponent<Props, Sta /> </div> {this.state.modal && ( - <AnalysisWarningsModal onClose={this.handleCloseModal} taskId={this.props.task.id} /> + <AnalysisWarningsModal onClose={this.handleCloseModal} warnings={this.props.warnings} /> )} </> ); diff --git a/server/sonar-web/src/main/js/app/components/nav/component/__tests__/ComponentNav-test.tsx b/server/sonar-web/src/main/js/app/components/nav/component/__tests__/ComponentNav-test.tsx index b0f633badb5..b06e39767ca 100644 --- a/server/sonar-web/src/main/js/app/components/nav/component/__tests__/ComponentNav-test.tsx +++ b/server/sonar-web/src/main/js/app/components/nav/component/__tests__/ComponentNav-test.tsx @@ -36,6 +36,7 @@ it('renders', () => { component={component} currentBranchLike={undefined} location={{}} + warnings={[]} /> ); wrapper.setState({ isInProgress: true, isPending: true }); diff --git a/server/sonar-web/src/main/js/app/components/nav/component/__tests__/ComponentNavMeta-test.tsx b/server/sonar-web/src/main/js/app/components/nav/component/__tests__/ComponentNavMeta-test.tsx index 74ad5181300..ea2e2016e25 100644 --- a/server/sonar-web/src/main/js/app/components/nav/component/__tests__/ComponentNavMeta-test.tsx +++ b/server/sonar-web/src/main/js/app/components/nav/component/__tests__/ComponentNavMeta-test.tsx @@ -52,6 +52,7 @@ it('renders status of short-living branch', () => { branchMeasures={MEASURES} component={COMPONENT} currentUser={{ isLoggedIn: false }} + warnings={[]} /> ) ).toMatchSnapshot(); @@ -70,6 +71,7 @@ it('renders meta for long-living branch', () => { branchLike={branch} component={COMPONENT} currentUser={{ isLoggedIn: false }} + warnings={[]} /> ) ).toMatchSnapshot(); @@ -90,6 +92,7 @@ it('renders meta for pull request', () => { branchLike={pullRequest} component={COMPONENT} currentUser={{ isLoggedIn: false }} + warnings={[]} /> ) ).toMatchSnapshot(); diff --git a/server/sonar-web/src/main/js/app/components/nav/component/__tests__/ComponentNavWarnings-test.tsx b/server/sonar-web/src/main/js/app/components/nav/component/__tests__/ComponentNavWarnings-test.tsx index 9eba3b71968..95c6431ba03 100644 --- a/server/sonar-web/src/main/js/app/components/nav/component/__tests__/ComponentNavWarnings-test.tsx +++ b/server/sonar-web/src/main/js/app/components/nav/component/__tests__/ComponentNavWarnings-test.tsx @@ -22,7 +22,7 @@ import { shallow } from 'enzyme'; import ComponentNavWarnings from '../ComponentNavWarnings'; it('should render', () => { - const wrapper = shallow(<ComponentNavWarnings task={{ id: 'abcd1234' }} />); + const wrapper = shallow(<ComponentNavWarnings warnings={['warning 1']} />); wrapper.setState({ modal: true }); expect(wrapper).toMatchSnapshot(); }); diff --git a/server/sonar-web/src/main/js/app/components/nav/component/__tests__/__snapshots__/ComponentNav-test.tsx.snap b/server/sonar-web/src/main/js/app/components/nav/component/__tests__/__snapshots__/ComponentNav-test.tsx.snap index b95d5c4732b..3e0cf719787 100644 --- a/server/sonar-web/src/main/js/app/components/nav/component/__tests__/__snapshots__/ComponentNav-test.tsx.snap +++ b/server/sonar-web/src/main/js/app/components/nav/component/__tests__/__snapshots__/ComponentNav-test.tsx.snap @@ -43,6 +43,7 @@ exports[`renders 1`] = ` "qualifier": "TRK", } } + warnings={Array []} /> </div> <ComponentNavMenu diff --git a/server/sonar-web/src/main/js/app/components/nav/component/__tests__/__snapshots__/ComponentNavWarnings-test.tsx.snap b/server/sonar-web/src/main/js/app/components/nav/component/__tests__/__snapshots__/ComponentNavWarnings-test.tsx.snap index 1f672b50096..02ba68a66f4 100644 --- a/server/sonar-web/src/main/js/app/components/nav/component/__tests__/__snapshots__/ComponentNavWarnings-test.tsx.snap +++ b/server/sonar-web/src/main/js/app/components/nav/component/__tests__/__snapshots__/ComponentNavWarnings-test.tsx.snap @@ -17,7 +17,7 @@ exports[`should render 1`] = ` href="#" onClick={[Function]} > - component_navigation.x_warnings.undefined + component_navigation.x_warnings.1 </a>, } } @@ -25,7 +25,11 @@ exports[`should render 1`] = ` </div> <LazyLoader onClose={[Function]} - taskId="abcd1234" + warnings={ + Array [ + "warning 1", + ] + } /> </React.Fragment> `; diff --git a/server/sonar-web/src/main/js/components/common/AnalysisWarningsModal.tsx b/server/sonar-web/src/main/js/components/common/AnalysisWarningsModal.tsx index 97af1814d03..b1fc7698a4f 100644 --- a/server/sonar-web/src/main/js/components/common/AnalysisWarningsModal.tsx +++ b/server/sonar-web/src/main/js/components/common/AnalysisWarningsModal.tsx @@ -27,7 +27,8 @@ import { getTask } from '../../api/ce'; interface Props { onClose: () => void; - taskId: string; + taskId?: string; + warnings?: string[]; } interface State { @@ -37,19 +38,25 @@ interface State { export default class AnalysisWarningsModal extends React.PureComponent<Props, State> { mounted = false; - state: State = { - loading: true, - warnings: [] - }; + + constructor(props: Props) { + super(props); + this.state = { loading: !props.warnings, warnings: props.warnings || [] }; + } componentDidMount() { this.mounted = true; - this.loadWarnings(); + if (!this.props.warnings && this.props.taskId) { + this.loadWarnings(this.props.taskId); + } } componentDidUpdate(prevProps: Props) { - if (prevProps.taskId !== this.props.taskId) { - this.loadWarnings(); + const { taskId, warnings } = this.props; + if (!warnings && taskId && prevProps.taskId !== taskId) { + this.loadWarnings(taskId); + } else if (warnings && prevProps.warnings !== warnings) { + this.setState({ warnings }); } } @@ -57,9 +64,9 @@ export default class AnalysisWarningsModal extends React.PureComponent<Props, St this.mounted = false; } - loadWarnings() { + loadWarnings(taskId: string) { this.setState({ loading: true }); - getTask(this.props.taskId, ['warnings']).then( + getTask(taskId, ['warnings']).then( ({ warnings = [] }) => { if (this.mounted) { this.setState({ loading: false, warnings }); diff --git a/server/sonar-web/src/main/js/components/common/__tests__/AnalysisWarningsModal-test.tsx b/server/sonar-web/src/main/js/components/common/__tests__/AnalysisWarningsModal-test.tsx index 51ffa11a653..017a937588a 100644 --- a/server/sonar-web/src/main/js/components/common/__tests__/AnalysisWarningsModal-test.tsx +++ b/server/sonar-web/src/main/js/components/common/__tests__/AnalysisWarningsModal-test.tsx @@ -24,16 +24,26 @@ import { waitAndUpdate } from '../../../helpers/testUtils'; import { getTask } from '../../../api/ce'; jest.mock('../../../api/ce', () => ({ - getTask: jest - .fn() - .mockResolvedValue({ - warnings: ['message foo', 'message-bar', 'multiline message\nsecondline\n third line'] - }) + getTask: jest.fn().mockResolvedValue({ + warnings: ['message foo', 'message-bar', 'multiline message\nsecondline\n third line'] + }) })); +beforeEach(() => { + (getTask as jest.Mock<any>).mockClear(); +}); + it('should fetch warnings and render', async () => { const wrapper = shallow(<AnalysisWarningsModal onClose={jest.fn()} taskId="abcd1234" />); await waitAndUpdate(wrapper); expect(wrapper).toMatchSnapshot(); expect(getTask).toBeCalledWith('abcd1234', ['warnings']); }); + +it('should render warnings without fetch', () => { + const wrapper = shallow( + <AnalysisWarningsModal onClose={jest.fn()} warnings={['warning 1', 'warning 2']} /> + ); + expect(wrapper).toMatchSnapshot(); + expect(getTask).not.toBeCalled(); +}); diff --git a/server/sonar-web/src/main/js/components/common/__tests__/__snapshots__/AnalysisWarningsModal-test.tsx.snap b/server/sonar-web/src/main/js/components/common/__tests__/__snapshots__/AnalysisWarningsModal-test.tsx.snap index c52023d0b5d..d11642c2553 100644 --- a/server/sonar-web/src/main/js/components/common/__tests__/__snapshots__/AnalysisWarningsModal-test.tsx.snap +++ b/server/sonar-web/src/main/js/components/common/__tests__/__snapshots__/AnalysisWarningsModal-test.tsx.snap @@ -90,3 +90,63 @@ exports[`should fetch warnings and render 1`] = ` </footer> </Modal> `; + +exports[`should render warnings without fetch 1`] = ` +<Modal + contentLabel="warnings" + onRequestClose={[MockFunction]} +> + <header + className="modal-head" + > + <h2> + warnings + </h2> + </header> + <div + className="modal-body modal-container js-analysis-warnings" + > + <DeferredSpinner + loading={false} + timeout={100} + > + <div + className="panel panel-vertical" + key="0" + > + <WarningIcon + className="pull-left spacer-right" + /> + <div + className="overflow-hidden markdown" + > + warning 1 + </div> + </div> + <div + className="panel panel-vertical" + key="1" + > + <WarningIcon + className="pull-left spacer-right" + /> + <div + className="overflow-hidden markdown" + > + warning 2 + </div> + </div> + </DeferredSpinner> + </div> + <footer + className="modal-foot" + > + <ResetButtonLink + className="js-modal-close" + onClick={[MockFunction]} + > + close + </ResetButtonLink> + </footer> +</Modal> +`; |