aboutsummaryrefslogtreecommitdiffstats
path: root/server
diff options
context:
space:
mode:
authorGrégoire Aubert <gregoire.aubert@sonarsource.com>2018-06-11 11:04:44 +0200
committerSonarTech <sonartech@sonarsource.com>2018-06-12 20:20:57 +0200
commit0a34e84cd5ccdbaff1f0b96262ccbe3345adf26f (patch)
tree0fc904d91c2563cdc019441c7b19ed2c5b7d8a10 /server
parentf3425772870c017497d30bcc5115b8b3d62f9fb2 (diff)
downloadsonarqube-0a34e84cd5ccdbaff1f0b96262ccbe3345adf26f.tar.gz
sonarqube-0a34e84cd5ccdbaff1f0b96262ccbe3345adf26f.zip
SONAR-9732 Automatically update project page when analysis is finished
Diffstat (limited to 'server')
-rw-r--r--server/sonar-web/src/main/js/app/components/ComponentContainer.tsx110
-rw-r--r--server/sonar-web/src/main/js/app/components/__tests__/ComponentContainer-test.tsx41
2 files changed, 111 insertions, 40 deletions
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 11d254b4ada..20bd441df41 100644
--- a/server/sonar-web/src/main/js/app/components/ComponentContainer.tsx
+++ b/server/sonar-web/src/main/js/app/components/ComponentContainer.tsx
@@ -20,6 +20,7 @@
import * as React from 'react';
import * as PropTypes from 'prop-types';
import { connect } from 'react-redux';
+import { differenceBy } from 'lodash';
import ComponentContainerNotFound from './ComponentContainerNotFound';
import ComponentNav from './nav/component/ComponentNav';
import { Component, BranchLike } from '../types';
@@ -47,14 +48,19 @@ interface Props {
}
interface State {
+ branchLike?: BranchLike;
branchLikes: BranchLike[];
component?: Component;
currentTask?: Task;
+ isPending: boolean;
loading: boolean;
- pendingTasks?: PendingTask[];
+ tasksInProgress?: PendingTask[];
}
+const FETCH_STATUS_WAIT_TIME = 3000;
+
export class ComponentContainer extends React.PureComponent<Props, State> {
+ watchStatusTimer?: number;
mounted = false;
static contextTypes = {
@@ -63,12 +69,12 @@ export class ComponentContainer extends React.PureComponent<Props, State> {
constructor(props: Props) {
super(props);
- this.state = { branchLikes: [], loading: true };
+ this.state = { branchLikes: [], isPending: false, loading: true };
}
componentDidMount() {
this.mounted = true;
- this.fetchComponent(this.props);
+ this.fetchComponent();
}
componentWillReceiveProps(nextProps: Props) {
@@ -90,7 +96,7 @@ export class ComponentContainer extends React.PureComponent<Props, State> {
qualifier: component.breadcrumbs[component.breadcrumbs.length - 1].qualifier
});
- fetchComponent(props: Props) {
+ fetchComponent(props = this.props) {
const { branch, id: key, pullRequest } = props.location.query;
this.setState({ loading: true });
@@ -114,49 +120,98 @@ export class ComponentContainer extends React.PureComponent<Props, State> {
this.props.fetchOrganizations([component.organization]);
}
- this.fetchBranches(component).then(branchLikes => {
+ this.fetchBranches(component).then(({ branchLike, branchLikes }) => {
if (this.mounted) {
- this.setState({ loading: false, branchLikes, component });
+ this.setState({ branchLike, branchLikes, component, loading: false });
+ this.fetchStatus(component);
}
}, onError);
-
- this.fetchStatus(component);
}, onError);
}
- fetchBranches = (component: Component): Promise<BranchLike[]> => {
+ fetchBranches = (
+ component: Component
+ ): Promise<{ branchLike?: BranchLike; branchLikes: BranchLike[] }> => {
const project = component.breadcrumbs.find(({ qualifier }) => qualifier === 'TRK');
return project
? Promise.all([getBranches(project.key), getPullRequests(project.key)]).then(
- ([branches, pullRequests]) => [...branches, ...pullRequests]
+ ([branches, pullRequests]) => {
+ const branchLikes = [...branches, ...pullRequests];
+ return {
+ branchLike: this.getCurrentBranchLike(branchLikes),
+ branchLikes
+ };
+ }
)
- : Promise.resolve([]);
+ : Promise.resolve({ branchLikes: [] });
};
fetchStatus = (component: Component) => {
getTasksForComponent(component.key).then(
({ current, queue }) => {
if (this.mounted) {
- this.setState({ currentTask: current, pendingTasks: queue });
+ let shouldFetchComponent = false;
+ this.setState(
+ ({ branchLike, component, currentTask, tasksInProgress }) => {
+ const newCurrentTask = this.getCurrentTask(current, branchLike);
+ const pendingTasks = this.getPendingTasks(queue, branchLike);
+ const newTasksInProgress = pendingTasks.filter(
+ task => task.status === STATUSES.IN_PROGRESS
+ );
+
+ const currentTaskChanged =
+ currentTask && newCurrentTask && currentTask.id !== newCurrentTask.id;
+ const progressChanged =
+ tasksInProgress &&
+ (newTasksInProgress.length !== tasksInProgress.length ||
+ differenceBy(newTasksInProgress, tasksInProgress, 'id').length > 0);
+
+ shouldFetchComponent = Boolean(currentTaskChanged || progressChanged);
+ if (!shouldFetchComponent && component && newTasksInProgress.length > 0) {
+ window.clearTimeout(this.watchStatusTimer);
+ this.watchStatusTimer = window.setTimeout(
+ () => this.fetchStatus(component),
+ FETCH_STATUS_WAIT_TIME
+ );
+ }
+
+ const isPending = pendingTasks.some(task => task.status === STATUSES.PENDING);
+ return {
+ currentTask: newCurrentTask,
+ isPending,
+ tasksInProgress: newTasksInProgress
+ };
+ },
+ () => {
+ if (shouldFetchComponent) {
+ this.fetchComponent();
+ }
+ }
+ );
}
},
() => {}
);
};
- getCurrentTask = (branchLike?: BranchLike) => {
- const { currentTask } = this.state;
- if (!currentTask) {
+ getCurrentBranchLike = (branchLikes: BranchLike[]) => {
+ const { query } = this.props.location;
+ return query.pullRequest
+ ? branchLikes.find(b => isPullRequest(b) && b.key === query.pullRequest)
+ : branchLikes.find(b => isBranch(b) && (query.branch ? b.name === query.branch : b.isMain));
+ };
+
+ getCurrentTask = (current: Task, branchLike?: BranchLike) => {
+ if (!current) {
return undefined;
}
- return currentTask.status === STATUSES.FAILED || this.isSameBranch(currentTask, branchLike)
- ? currentTask
+ return current.status === STATUSES.FAILED || this.isSameBranch(current, branchLike)
+ ? current
: undefined;
};
- getPendingTasks = (branchLike?: BranchLike) => {
- const { pendingTasks = [] } = this.state;
+ getPendingTasks = (pendingTasks: PendingTask[], branchLike?: BranchLike) => {
return pendingTasks.filter(task => this.isSameBranch(task, branchLike));
};
@@ -184,9 +239,9 @@ export class ComponentContainer extends React.PureComponent<Props, State> {
handleBranchesChange = () => {
if (this.mounted && this.state.component) {
this.fetchBranches(this.state.component).then(
- branchLikes => {
+ ({ branchLike, branchLikes }) => {
if (this.mounted) {
- this.setState({ branchLikes });
+ this.setState({ branchLike, branchLikes });
}
},
() => {}
@@ -195,21 +250,14 @@ export class ComponentContainer extends React.PureComponent<Props, State> {
};
render() {
- const { query } = this.props.location;
- const { branchLikes, component, loading } = this.state;
+ const { component, loading } = this.state;
if (!loading && !component) {
return <ComponentContainerNotFound />;
}
- const branchLike = query.pullRequest
- ? branchLikes.find(b => isPullRequest(b) && b.key === query.pullRequest)
- : branchLikes.find(b => isBranch(b) && (query.branch ? b.name === query.branch : b.isMain));
-
- const currentTask = this.getCurrentTask(branchLike);
- const pendingTasks = this.getPendingTasks(branchLike);
- const isInProgress = pendingTasks.some(task => task.status === STATUSES.IN_PROGRESS);
- const isPending = pendingTasks.some(task => task.status === STATUSES.PENDING);
+ const { branchLike, branchLikes, currentTask, isPending, tasksInProgress } = this.state;
+ const isInProgress = tasksInProgress && tasksInProgress.length > 0;
return (
<div>
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 eda3927faf1..9052c611e68 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
@@ -32,6 +32,7 @@ import {
BranchType
} from '../../types';
import { STATUSES } from '../../../apps/background-tasks/constants';
+import { waitAndUpdate } from '../../../helpers/testUtils';
jest.mock('../../../api/branches', () => ({
getBranches: jest.fn(() => Promise.resolve([])),
@@ -229,13 +230,35 @@ it('filters correctly the pending tasks for a main branch', () => {
{ branch: 'feature', branchType: 'SHORT' } as Task,
{} as Task
];
- expect(component.getCurrentTask(undefined)).toBe(undefined);
- component.setState({ currentTask: failedTask });
- expect(component.getCurrentTask(mainBranch)).toBe(failedTask);
- component.setState({ currentTask });
- expect(component.getCurrentTask(mainBranch)).toBe(undefined);
- expect(component.getCurrentTask(pullRequest)).toMatchObject(currentTask);
- component.setState({ pendingTasks });
- expect(component.getPendingTasks(mainBranch)).toMatchObject([{}]);
- expect(component.getPendingTasks(pullRequest)).toMatchObject([currentTask]);
+ expect(component.getCurrentTask(currentTask, undefined)).toBe(undefined);
+ expect(component.getCurrentTask(failedTask, mainBranch)).toBe(failedTask);
+ expect(component.getCurrentTask(currentTask, mainBranch)).toBe(undefined);
+ expect(component.getCurrentTask(currentTask, pullRequest)).toMatchObject(currentTask);
+ expect(component.getPendingTasks(pendingTasks, mainBranch)).toMatchObject([{}]);
+ expect(component.getPendingTasks(pendingTasks, pullRequest)).toMatchObject([currentTask]);
+});
+
+it('reload component after task progress finished', async () => {
+ jest.useFakeTimers();
+ const inProgressTask = { id: 'foo', status: STATUSES.IN_PROGRESS } as Task;
+ (getTasksForComponent as jest.Mock<any>).mockResolvedValueOnce({ queue: [inProgressTask] });
+ const wrapper = shallow(
+ <ComponentContainer fetchOrganizations={jest.fn()} location={{ query: { id: 'foo' } }}>
+ <Inner />
+ </ComponentContainer>
+ );
+ await waitAndUpdate(wrapper);
+ expect(getComponentNavigation).toHaveBeenCalledTimes(1);
+ expect(getTasksForComponent).toHaveBeenCalledTimes(1);
+
+ jest.runAllTimers();
+ expect(getTasksForComponent).toHaveBeenCalledTimes(2);
+ await waitAndUpdate(wrapper);
+ expect(getComponentNavigation).toHaveBeenCalledTimes(2);
+ expect(getTasksForComponent).toHaveBeenCalledTimes(3);
+
+ jest.runAllTimers();
+ await waitAndUpdate(wrapper);
+ expect(getComponentNavigation).toHaveBeenCalledTimes(2);
+ expect(getTasksForComponent).toHaveBeenCalledTimes(3);
});