Explorar el Código

SONAR-12040 Display pending time on system background task page

tags/7.8
Grégoire Aubert hace 5 años
padre
commit
ac64b4fe18
Se han modificado 19 ficheros con 805 adiciones y 250 borrados
  1. 0
    5
      server/sonar-web/src/main/js/app/styles/init/type.css
  2. 0
    95
      server/sonar-web/src/main/js/apps/background-tasks/__tests__/BackgroundTasks-test.tsx
  3. 5
    0
      server/sonar-web/src/main/js/apps/background-tasks/background-tasks.css
  4. 33
    41
      server/sonar-web/src/main/js/apps/background-tasks/components/BackgroundTasksApp.tsx
  5. 69
    0
      server/sonar-web/src/main/js/apps/background-tasks/components/StatPendingCount.tsx
  6. 49
    0
      server/sonar-web/src/main/js/apps/background-tasks/components/StatPendingTime.tsx
  7. 52
    0
      server/sonar-web/src/main/js/apps/background-tasks/components/StatStillFailing.tsx
  8. 28
    103
      server/sonar-web/src/main/js/apps/background-tasks/components/Stats.tsx
  9. 100
    0
      server/sonar-web/src/main/js/apps/background-tasks/components/__tests__/BackgroundTasksApp-test.tsx
  10. 62
    0
      server/sonar-web/src/main/js/apps/background-tasks/components/__tests__/StatPendingCount-test.tsx
  11. 49
    0
      server/sonar-web/src/main/js/apps/background-tasks/components/__tests__/StatPendingTime-test.tsx
  12. 47
    0
      server/sonar-web/src/main/js/apps/background-tasks/components/__tests__/StatStillFailing-test.tsx
  13. 22
    6
      server/sonar-web/src/main/js/apps/background-tasks/components/__tests__/Stats-test.tsx
  14. 168
    0
      server/sonar-web/src/main/js/apps/background-tasks/components/__tests__/__snapshots__/BackgroundTasksApp-test.tsx.snap
  15. 26
    0
      server/sonar-web/src/main/js/apps/background-tasks/components/__tests__/__snapshots__/StatPendingCount-test.tsx.snap
  16. 20
    0
      server/sonar-web/src/main/js/apps/background-tasks/components/__tests__/__snapshots__/StatPendingTime-test.tsx.snap
  17. 40
    0
      server/sonar-web/src/main/js/apps/background-tasks/components/__tests__/__snapshots__/StatStillFailing-test.tsx.snap
  18. 33
    0
      server/sonar-web/src/main/js/apps/background-tasks/components/__tests__/__snapshots__/Stats-test.tsx.snap
  19. 2
    0
      sonar-core/src/main/resources/org/sonar/l10n/core.properties

+ 0
- 5
server/sonar-web/src/main/js/app/styles/init/type.css Ver fichero

@@ -146,11 +146,6 @@ mark {
font-weight: bold;
}

.emphasised-measure {
font-size: 24px;
font-weight: 300;
}

blockquote {
border-left: 3px solid var(--barBorderColor);
padding: 0 8px;

+ 0
- 95
server/sonar-web/src/main/js/apps/background-tasks/__tests__/BackgroundTasks-test.tsx Ver fichero

@@ -19,14 +19,11 @@
*/
import * as React from 'react';
import { shallow } from 'enzyme';
import Stats from '../components/Stats';
import Search from '../components/Search';
import { STATUSES, CURRENTS, DEBOUNCE_DELAY, DEFAULT_FILTERS } from '../constants';
import { formatDuration } from '../utils';
import { click } from '../../../helpers/testUtils';

const stub = jest.fn();

describe('Constants', () => {
it('should have STATUSES', () => {
expect(Object.keys(STATUSES).length).toBe(7);
@@ -79,98 +76,6 @@ describe('Search', () => {
});
});

describe('Stats', () => {
describe('Pending', () => {
it('should show zero pending', () => {
const result = shallow(
<Stats onCancelAllPending={stub} onShowFailing={stub} pendingCount={0} />
);
expect(result.find('.js-pending-count').text()).toContain('0');
});

it('should show 5 pending', () => {
const result = shallow(
<Stats onCancelAllPending={stub} onShowFailing={stub} pendingCount={5} />
);
expect(result.find('.js-pending-count').text()).toContain('5');
});

it('should not show cancel pending button', () => {
const result = shallow(
<Stats onCancelAllPending={stub} onShowFailing={stub} pendingCount={0} />
);
expect(result.find('[data-test="cancel-pending"]').length).toBe(0);
});

it('should show cancel pending button', () => {
const result = shallow(
<Stats
isSystemAdmin={true}
onCancelAllPending={stub}
onShowFailing={stub}
pendingCount={5}
/>
);
expect(result.find('[data-test="cancel-pending"]').length).toBe(1);
});

it('should trigger cancelling pending', () => {
const spy = jest.fn();
const result = shallow(
<Stats
isSystemAdmin={true}
onCancelAllPending={spy}
onShowFailing={stub}
pendingCount={5}
/>
);
expect(spy).not.toBeCalled();
result.find('[data-test="cancel-pending"]').prop<Function>('onConfirm')();
expect(spy).toBeCalled();
});
});

describe('Failures', () => {
it('should show zero failures', () => {
const result = shallow(
<Stats failingCount={0} onCancelAllPending={stub} onShowFailing={stub} />
);
expect(result.find('.js-failures-count').text()).toContain('0');
});

it('should show 5 failures', () => {
const result = shallow(
<Stats failingCount={5} onCancelAllPending={stub} onShowFailing={stub} />
);
expect(result.find('.js-failures-count').text()).toContain('5');
});

it('should not show link to failures', () => {
const result = shallow(
<Stats failingCount={0} onCancelAllPending={stub} onShowFailing={stub} />
);
expect(result.find('.js-failures-count').is('a')).toBeFalsy();
});

it('should show link to failures', () => {
const result = shallow(
<Stats failingCount={5} onCancelAllPending={stub} onShowFailing={stub} />
);
expect(result.find('.js-failures-count').is('a')).toBeTruthy();
});

it('should trigger filtering failures', () => {
const spy = jest.fn();
const result = shallow(
<Stats failingCount={5} onCancelAllPending={stub} onShowFailing={spy} />
);
expect(spy).not.toBeCalled();
click(result.find('.js-failures-count'));
expect(spy).toBeCalled();
});
});
});

describe('Helpers', () => {
describe('#formatDuration()', () => {
it('should format 173ms', () => {

+ 5
- 0
server/sonar-web/src/main/js/apps/background-tasks/background-tasks.css Ver fichero

@@ -41,3 +41,8 @@
.bt-workers-warning-icon {
margin-top: 5px;
}

.emphasised-measure {
font-size: var(--hugeFontSize);
font-weight: 300;
}

+ 33
- 41
server/sonar-web/src/main/js/apps/background-tasks/components/BackgroundTasksApp.tsx Ver fichero

@@ -21,15 +21,14 @@ import * as React from 'react';
import Helmet from 'react-helmet';
import { debounce, uniq } from 'lodash';
import { connect } from 'react-redux';
import { InjectedRouter } from 'react-router';
import { Location } from 'history';
import Header from './Header';
import Footer from './Footer';
import StatsContainer from './StatsContainer';
import Stats from './Stats';
import Search from './Search';
import Tasks from './Tasks';
import { DEFAULT_FILTERS, DEBOUNCE_DELAY, STATUSES, CURRENTS } from '../constants';
import Suggestions from '../../../app/components/embed-docs-modal/Suggestions';
import { Location, Router } from '../../../components/hoc/withRouter';
import {
getTypes,
getActivity,
@@ -45,34 +44,28 @@ import { toShortNotSoISOString } from '../../../helpers/dates';
import '../background-tasks.css';

interface Props {
component?: { id: string };
component?: Pick<T.Component, 'key'> & { id: string }; // id should be removed when api/ce/activity accept a component key instead of an id
fetchOrganizations: (keys: string[]) => void;
location: Location;
router: Pick<InjectedRouter, 'push'>;
router: Pick<Router, 'push'>;
}

interface State {
failingCount: number;
loading: boolean;
pendingCount: number;
pendingTime?: number;
tasks: T.Task[];
types?: string[];
query: string;
pendingCount: number;
failingCount: number;
}

class BackgroundTasksApp extends React.PureComponent<Props, State> {
export class BackgroundTasksApp extends React.PureComponent<Props, State> {
loadTasksDebounced: () => void;
mounted = false;

constructor(props: Props) {
super(props);
this.state = {
failingCount: 0,
loading: true,
pendingCount: 0,
query: '',
tasks: []
};
this.state = { failingCount: 0, loading: true, pendingCount: 0, tasks: [] };
this.loadTasksDebounced = debounce(this.loadTasks, DEBOUNCE_DELAY);
}

@@ -125,25 +118,23 @@ class BackgroundTasksApp extends React.PureComponent<Props, State> {
parameters.componentId = this.props.component.id;
}

Promise.all([getActivity(parameters), getStatus(parameters.componentId)]).then(responses => {
if (this.mounted) {
const [activity, status] = responses;
const { tasks } = activity;

const pendingCount = status.pending;
const failingCount = status.failing;

const organizations = uniq(tasks.map(task => task.organization).filter(o => o));
this.props.fetchOrganizations(organizations);

this.setState({
tasks,
pendingCount,
failingCount,
loading: false
});
}
}, this.stopLoading);
Promise.all([getActivity(parameters), getStatus(parameters.componentId)]).then(
([{ tasks }, status]) => {
if (this.mounted) {
const organizations = uniq(tasks.map(task => task.organization).filter(o => o));
this.props.fetchOrganizations(organizations);

this.setState({
failingCount: status.failing,
loading: false,
pendingCount: status.pending,
pendingTime: status.pendingTime,
tasks
});
}
},
this.stopLoading
);
};

handleFilterUpdate = (nextState: Partial<Query>) => {
@@ -187,13 +178,13 @@ class BackgroundTasksApp extends React.PureComponent<Props, State> {
this.handleFilterUpdate({ query: task.componentKey });
};

handleShowFailing() {
handleShowFailing = () => {
this.handleFilterUpdate({
...DEFAULT_FILTERS,
status: STATUSES.FAILED,
currents: CURRENTS.ONLY_CURRENTS
});
}
};

handleCancelAllPending = () => {
this.setState({ loading: true });
@@ -207,7 +198,7 @@ class BackgroundTasksApp extends React.PureComponent<Props, State> {

render() {
const { component } = this.props;
const { loading, types, tasks, pendingCount, failingCount } = this.state;
const { loading, types, tasks } = this.state;

if (!types) {
return (
@@ -230,12 +221,13 @@ class BackgroundTasksApp extends React.PureComponent<Props, State> {
<Helmet title={translate('background_tasks.page')} />
<Header component={component} />

<StatsContainer
<Stats
component={component}
failingCount={failingCount}
failingCount={this.state.failingCount}
onCancelAllPending={this.handleCancelAllPending}
onShowFailing={this.handleShowFailing}
pendingCount={pendingCount}
pendingCount={this.state.pendingCount}
pendingTime={this.state.pendingTime}
/>

<Search

+ 69
- 0
server/sonar-web/src/main/js/apps/background-tasks/components/StatPendingCount.tsx Ver fichero

@@ -0,0 +1,69 @@
/*
* SonarQube
* Copyright (C) 2009-2019 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 * as React from 'react';
import { connect } from 'react-redux';
import * as theme from '../../../app/theme';
import { ClearButton } from '../../../components/ui/buttons';
import ConfirmButton from '../../../components/controls/ConfirmButton';
import Tooltip from '../../../components/controls/Tooltip';
import { translate } from '../../../helpers/l10n';
import { getAppState, Store } from '../../../store/rootReducer';

export interface Props {
isSystemAdmin?: boolean;
onCancelAllPending: () => void;
pendingCount?: number;
}

export function StatPendingCount({ isSystemAdmin, onCancelAllPending, pendingCount }: Props) {
if (pendingCount === undefined) {
return null;
}

return (
<span>
<span className="emphasised-measure">{pendingCount}</span>
<span className="little-spacer-left display-inline-flex-center">
{translate('background_tasks.pending')}
{isSystemAdmin && pendingCount > 0 && (
<ConfirmButton
cancelButtonText={translate('close')}
confirmButtonText={translate('background_tasks.cancel_all_tasks.submit')}
isDestructive={true}
modalBody={translate('background_tasks.cancel_all_tasks.text')}
modalHeader={translate('background_tasks.cancel_all_tasks')}
onConfirm={onCancelAllPending}>
{({ onClick }) => (
<Tooltip overlay={translate('background_tasks.cancel_all_tasks')}>
<ClearButton className="little-spacer-left" color={theme.red} onClick={onClick} />
</Tooltip>
)}
</ConfirmButton>
)}
</span>
</span>
);
}

const mapStateToProps = (state: Store) => ({
isSystemAdmin: getAppState(state).canAdmin
});

export default connect(mapStateToProps)(StatPendingCount);

+ 49
- 0
server/sonar-web/src/main/js/apps/background-tasks/components/StatPendingTime.tsx Ver fichero

@@ -0,0 +1,49 @@
/*
* SonarQube
* Copyright (C) 2009-2019 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 * as React from 'react';
import HelpTooltip from '../../../components/controls/HelpTooltip';
import { translate } from '../../../helpers/l10n';
import { formatMeasure } from '../../../helpers/measures';

// Do not display the pending time for values smaller than this threshold (in ms)
const MIN_PENDING_TIME_THRESHOLD = 1000;

export interface Props {
className?: string;
component?: Pick<T.Component, 'key'>;
pendingCount?: number;
pendingTime?: number;
}

export default function StatPendingTime({ className, pendingCount, pendingTime }: Props) {
if (!pendingTime || !pendingCount || pendingTime < MIN_PENDING_TIME_THRESHOLD) {
return null;
}
return (
<span className={className}>
<span className="emphasised-measure">{formatMeasure(pendingTime, 'MILLISEC')}</span>
<span className="little-spacer-left">{translate('background_tasks.pending_time')}</span>
<HelpTooltip
className="little-spacer-left"
overlay={translate('background_tasks.pending_time.description')}
/>
</span>
);
}

+ 52
- 0
server/sonar-web/src/main/js/apps/background-tasks/components/StatStillFailing.tsx Ver fichero

@@ -0,0 +1,52 @@
/*
* SonarQube
* Copyright (C) 2009-2019 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 * as React from 'react';
import { ButtonLink } from '../../../components/ui/buttons';
import { translate } from '../../../helpers/l10n';
import HelpTooltip from '../../../components/controls/HelpTooltip';

export interface Props {
className?: string;
failingCount?: number;
onShowFailing: () => void;
}

export default function StatStillFailing({ className, failingCount, onShowFailing }: Props) {
if (failingCount === undefined) {
return null;
}

return (
<span className={className}>
{failingCount > 0 ? (
<ButtonLink className="emphasised-measure text-baseline" onClick={onShowFailing}>
{failingCount}
</ButtonLink>
) : (
<span className="emphasised-measure">{failingCount}</span>
)}
<span className="little-spacer-left">{translate('background_tasks.failures')}</span>
<HelpTooltip
className="little-spacer-left"
overlay={translate('background_tasks.failing_count')}
/>
</span>
);
}

+ 28
- 103
server/sonar-web/src/main/js/apps/background-tasks/components/Stats.tsx Ver fichero

@@ -18,112 +18,37 @@
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
import * as React from 'react';
import Tooltip from '../../../components/controls/Tooltip';
import { DeleteButton } from '../../../components/ui/buttons';
import { translate } from '../../../helpers/l10n';
import ConfirmButton from '../../../components/controls/ConfirmButton';
import StatPendingCount from './StatPendingCount';
import StatPendingTime from './StatPendingTime';
import StatStillFailing from './StatStillFailing';

interface Props {
component?: unknown;
export interface Props {
component?: Pick<T.Component, 'key'>;
failingCount?: number;
isSystemAdmin?: boolean;
pendingCount?: number;
onShowFailing: () => void;
onCancelAllPending: () => void;
onShowFailing: () => void;
pendingCount?: number;
pendingTime?: number;
}

export default class Stats extends React.PureComponent<Props> {
handleShowFailing = (event: React.MouseEvent<HTMLAnchorElement>) => {
event.preventDefault();
event.currentTarget.blur();
this.props.onShowFailing();
};

renderPending() {
if (this.props.pendingCount === undefined) {
return null;
}
if (this.props.pendingCount > 0) {
return (
<span>
<span className="js-pending-count emphasised-measure">{this.props.pendingCount}</span>
<span className="display-inline-flex-center little-spacer-left">
{translate('background_tasks.pending')}
{this.props.isSystemAdmin && (
<ConfirmButton
cancelButtonText={translate('close')}
confirmButtonText={translate('background_tasks.cancel_all_tasks.submit')}
data-test="cancel-pending"
isDestructive={true}
modalBody={translate('background_tasks.cancel_all_tasks.text')}
modalHeader={translate('background_tasks.cancel_all_tasks')}
onConfirm={this.props.onCancelAllPending}>
{({ onClick }) => (
<Tooltip overlay={translate('background_tasks.cancel_all_tasks')}>
<DeleteButton
className="js-cancel-pending little-spacer-left"
onClick={onClick}
/>
</Tooltip>
)}
</ConfirmButton>
)}
</span>
</span>
);
} else {
return (
<span>
<span className="js-pending-count emphasised-measure">{this.props.pendingCount}</span>
&nbsp;
{translate('background_tasks.pending')}
</span>
);
}
}

renderFailures() {
if (this.props.failingCount === undefined) {
return null;
}

if (this.props.component) {
return null;
}

if (this.props.failingCount > 0) {
return (
<span>
<Tooltip overlay={translate('background_tasks.failing_count')}>
<a
className="js-failures-count emphasised-measure"
href="#"
onClick={this.handleShowFailing}>
{this.props.failingCount}
</a>
</Tooltip>
&nbsp;
{translate('background_tasks.failures')}
</span>
);
} else {
return (
<span>
<Tooltip overlay={translate('background_tasks.failing_count')}>
<span className="js-failures-count emphasised-measure">{this.props.failingCount}</span>
</Tooltip>
<span className="little-spacer-left">{translate('background_tasks.failures')}</span>
</span>
);
}
}

render() {
return (
<section className="big-spacer-top big-spacer-bottom">
<span>{this.renderPending()}</span>
<span className="huge-spacer-left">{this.renderFailures()}</span>
</section>
);
}
export default function Stats({ component, pendingCount, pendingTime, ...props }: Props) {
return (
<section className="big-spacer-top big-spacer-bottom">
<StatPendingCount onCancelAllPending={props.onCancelAllPending} pendingCount={pendingCount} />
{!component && (
<StatPendingTime
className="huge-spacer-left"
pendingCount={pendingCount}
pendingTime={pendingTime}
/>
)}
{!component && (
<StatStillFailing
className="huge-spacer-left"
failingCount={props.failingCount}
onShowFailing={props.onShowFailing}
/>
)}
</section>
);
}

+ 100
- 0
server/sonar-web/src/main/js/apps/background-tasks/components/__tests__/BackgroundTasksApp-test.tsx Ver fichero

@@ -0,0 +1,100 @@
/*
* SonarQube
* Copyright (C) 2009-2019 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 * as React from 'react';
import { shallow } from 'enzyme';
import { BackgroundTasksApp } from '../BackgroundTasksApp';
import { mockLocation, mockRouter } from '../../../../helpers/testMocks';
import { waitAndUpdate } from '../../../../helpers/testUtils';

jest.mock('../../../../api/ce', () => ({
getTypes: jest.fn().mockResolvedValue({
taskTypes: ['REPORT', 'PROJECT_EXPORT', 'PROJECT_IMPORT', 'VIEW_REFRESH']
}),
getActivity: jest.fn().mockResolvedValue({
tasks: [
{
id: 'AWkGcOThOiAPiP5AE-kM',
type: 'VIEW_REFRESH',
componentId: 'AWBLZYhGOUrjxRA-u6ex',
componentKey: 'sonar-csharp',
componentName: 'SonarC#',
componentQualifier: 'APP',
status: 'FAILED',
submittedAt: '2019-02-19T16:47:35+0100',
startedAt: '2019-02-19T16:47:36+0100',
executedAt: '2019-02-19T16:47:36+0100',
executionTimeMs: 16,
logs: false,
errorMessage:
'Analyses suspended. Please set a valid license for the Edition you installed.',
hasScannerContext: false,
organization: 'default-organization',
errorType: 'LICENSING',
warningCount: 0,
warnings: []
},
{
id: 'AWkGcOThOiAPiP5AE-kL',
type: 'VIEW_REFRESH',
componentId: 'AV2ZaHs1Wa2znA6pDz1l',
componentKey: 'c-cpp-build-wrapper',
componentName: 'C/C++ Build Wrapper',
componentQualifier: 'APP',
status: 'SUCCESS',
submittedAt: '2019-02-19T16:47:35+0100',
startedAt: '2019-02-19T16:47:36+0100',
executedAt: '2019-02-19T16:47:36+0100',
executionTimeMs: 19,
logs: false,
hasScannerContext: false,
organization: 'default-organization',
warningCount: 0,
warnings: []
}
]
}),
getStatus: jest.fn().mockResolvedValue({ pending: 0, failing: 15, inProgress: 0 }),
cancelAllTasks: jest.fn().mockResolvedValue({}),
cancelTask: jest.fn().mockResolvedValue({})
}));

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

it('should render correctly', async () => {
const wrapper = shallowRender();
expect(wrapper).toMatchSnapshot();

await waitAndUpdate(wrapper);
expect(wrapper).toMatchSnapshot();
});

function shallowRender(props: Partial<BackgroundTasksApp['props']> = {}) {
return shallow(
<BackgroundTasksApp
component={{ key: 'foo', id: '564' }}
fetchOrganizations={jest.fn()}
location={mockLocation()}
router={mockRouter()}
{...props}
/>
);
}

+ 62
- 0
server/sonar-web/src/main/js/apps/background-tasks/components/__tests__/StatPendingCount-test.tsx Ver fichero

@@ -0,0 +1,62 @@
/*
* SonarQube
* Copyright (C) 2009-2019 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 * as React from 'react';
import { shallow } from 'enzyme';
import { StatPendingCount, Props } from '../StatPendingCount';

it('should render correctly', () => {
expect(shallowRender()).toMatchSnapshot();
});

it('should not render', () => {
expect(shallowRender({ pendingCount: undefined }).type()).toBeNull();
});

it('should not show cancel pending button', () => {
expect(
shallowRender({ pendingCount: 0 })
.find('ConfirmButton')
.exists()
).toBe(false);
expect(
shallowRender({ isSystemAdmin: false })
.find('ConfirmButton')
.exists()
).toBe(false);
});

it('should trigger cancelling pending', () => {
const onCancelAllPending = jest.fn();
const result = shallowRender({ onCancelAllPending });
expect(onCancelAllPending).not.toBeCalled();
result.find('ConfirmButton').prop<Function>('onConfirm')();
expect(onCancelAllPending).toBeCalled();
});

function shallowRender(props: Partial<Props> = {}) {
return shallow(
<StatPendingCount
isSystemAdmin={true}
onCancelAllPending={jest.fn()}
pendingCount={5}
{...props}
/>
);
}

+ 49
- 0
server/sonar-web/src/main/js/apps/background-tasks/components/__tests__/StatPendingTime-test.tsx Ver fichero

@@ -0,0 +1,49 @@
/*
* SonarQube
* Copyright (C) 2009-2019 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 * as React from 'react';
import { shallow } from 'enzyme';
import StatPendingTime, { Props } from '../StatPendingTime';

it('should render correctly', () => {
expect(shallowRender()).toMatchSnapshot();
});

it('should not render', () => {
expect(shallowRender({ pendingCount: undefined }).type()).toBeNull();
expect(shallowRender({ pendingCount: 0 }).type()).toBeNull();
expect(shallowRender({ pendingTime: undefined }).type()).toBeNull();
});

it('should not render when pending time is too small', () => {
expect(
shallowRender({ pendingTime: 0 })
.find('.emphasised-measure')
.exists()
).toBe(false);
expect(
shallowRender({ pendingTime: 900 })
.find('.emphasised-measure')
.exists()
).toBe(false);
});

function shallowRender(props: Partial<Props> = {}) {
return shallow(<StatPendingTime pendingCount={5} pendingTime={15420} {...props} />);
}

+ 47
- 0
server/sonar-web/src/main/js/apps/background-tasks/components/__tests__/StatStillFailing-test.tsx Ver fichero

@@ -0,0 +1,47 @@
/*
* SonarQube
* Copyright (C) 2009-2019 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 * as React from 'react';
import { shallow } from 'enzyme';
import StatStillFailing, { Props } from '../StatStillFailing';
import { click } from '../../../../helpers/testUtils';

it('should render correctly', () => {
expect(shallowRender()).toMatchSnapshot();
});

it('should not render', () => {
expect(shallowRender({ failingCount: undefined }).type()).toBeNull();
});

it('should render without the filter link', () => {
expect(shallowRender({ failingCount: 0 })).toMatchSnapshot();
});

it('should trigger filtering failures', () => {
const onShowFailing = jest.fn();
const result = shallowRender({ onShowFailing });
expect(onShowFailing).not.toBeCalled();
click(result.find('ButtonLink'));
expect(onShowFailing).toBeCalled();
});

function shallowRender(props: Partial<Props> = {}) {
return shallow(<StatStillFailing failingCount={5} onShowFailing={jest.fn()} {...props} />);
}

server/sonar-web/src/main/js/apps/background-tasks/components/StatsContainer.tsx → server/sonar-web/src/main/js/apps/background-tasks/components/__tests__/Stats-test.tsx Ver fichero

@@ -17,12 +17,28 @@
* along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
import { connect } from 'react-redux';
import Stats from './Stats';
import { getAppState, Store } from '../../../store/rootReducer';
import * as React from 'react';
import { shallow } from 'enzyme';
import Stats, { Props } from '../Stats';
import { mockComponent } from '../../../../helpers/testMocks';

const mapStateToProps = (state: Store) => ({
isSystemAdmin: !!getAppState(state).canAdmin
it('should render correctly', () => {
expect(shallowRender()).toMatchSnapshot();
});

export default connect(mapStateToProps)(Stats);
it('should render correctly for a component', () => {
expect(shallowRender({ component: mockComponent() })).toMatchSnapshot();
});

function shallowRender(props: Partial<Props> = {}) {
return shallow(
<Stats
failingCount={4}
onCancelAllPending={jest.fn()}
onShowFailing={jest.fn()}
pendingCount={2}
pendingTime={110545}
{...props}
/>
);
}

+ 168
- 0
server/sonar-web/src/main/js/apps/background-tasks/components/__tests__/__snapshots__/BackgroundTasksApp-test.tsx.snap Ver fichero

@@ -0,0 +1,168 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP

exports[`should render correctly 1`] = `
<div
className="page page-limited"
>
<i
className="spinner"
/>
</div>
`;

exports[`should render correctly 2`] = `
<div
className="page page-limited"
>
<Suggestions
suggestions="background_tasks"
/>
<HelmetWrapper
defer={true}
encodeSpecialCharacters={true}
title="background_tasks.page"
/>
<Header
component={
Object {
"id": "564",
"key": "foo",
}
}
/>
<Stats
component={
Object {
"id": "564",
"key": "foo",
}
}
failingCount={15}
onCancelAllPending={[Function]}
onShowFailing={[Function]}
pendingCount={0}
/>
<Search
component={
Object {
"id": "564",
"key": "foo",
}
}
currents="__ALL__"
loading={false}
onFilterUpdate={[Function]}
onReload={[Function]}
query=""
status="__ALL_EXCEPT_PENDING__"
taskType="ALL_TYPES"
types={
Object {
"taskTypes": Array [
"REPORT",
"PROJECT_EXPORT",
"PROJECT_IMPORT",
"VIEW_REFRESH",
],
}
}
/>
<Tasks
component={
Object {
"id": "564",
"key": "foo",
}
}
loading={false}
onCancelTask={[Function]}
onFilterTask={[Function]}
tasks={
Array [
Object {
"componentId": "AWBLZYhGOUrjxRA-u6ex",
"componentKey": "sonar-csharp",
"componentName": "SonarC#",
"componentQualifier": "APP",
"errorMessage": "Analyses suspended. Please set a valid license for the Edition you installed.",
"errorType": "LICENSING",
"executedAt": "2019-02-19T16:47:36+0100",
"executionTimeMs": 16,
"hasScannerContext": false,
"id": "AWkGcOThOiAPiP5AE-kM",
"logs": false,
"organization": "default-organization",
"startedAt": "2019-02-19T16:47:36+0100",
"status": "FAILED",
"submittedAt": "2019-02-19T16:47:35+0100",
"type": "VIEW_REFRESH",
"warningCount": 0,
"warnings": Array [],
},
Object {
"componentId": "AV2ZaHs1Wa2znA6pDz1l",
"componentKey": "c-cpp-build-wrapper",
"componentName": "C/C++ Build Wrapper",
"componentQualifier": "APP",
"executedAt": "2019-02-19T16:47:36+0100",
"executionTimeMs": 19,
"hasScannerContext": false,
"id": "AWkGcOThOiAPiP5AE-kL",
"logs": false,
"organization": "default-organization",
"startedAt": "2019-02-19T16:47:36+0100",
"status": "SUCCESS",
"submittedAt": "2019-02-19T16:47:35+0100",
"type": "VIEW_REFRESH",
"warningCount": 0,
"warnings": Array [],
},
]
}
/>
<Footer
tasks={
Array [
Object {
"componentId": "AWBLZYhGOUrjxRA-u6ex",
"componentKey": "sonar-csharp",
"componentName": "SonarC#",
"componentQualifier": "APP",
"errorMessage": "Analyses suspended. Please set a valid license for the Edition you installed.",
"errorType": "LICENSING",
"executedAt": "2019-02-19T16:47:36+0100",
"executionTimeMs": 16,
"hasScannerContext": false,
"id": "AWkGcOThOiAPiP5AE-kM",
"logs": false,
"organization": "default-organization",
"startedAt": "2019-02-19T16:47:36+0100",
"status": "FAILED",
"submittedAt": "2019-02-19T16:47:35+0100",
"type": "VIEW_REFRESH",
"warningCount": 0,
"warnings": Array [],
},
Object {
"componentId": "AV2ZaHs1Wa2znA6pDz1l",
"componentKey": "c-cpp-build-wrapper",
"componentName": "C/C++ Build Wrapper",
"componentQualifier": "APP",
"executedAt": "2019-02-19T16:47:36+0100",
"executionTimeMs": 19,
"hasScannerContext": false,
"id": "AWkGcOThOiAPiP5AE-kL",
"logs": false,
"organization": "default-organization",
"startedAt": "2019-02-19T16:47:36+0100",
"status": "SUCCESS",
"submittedAt": "2019-02-19T16:47:35+0100",
"type": "VIEW_REFRESH",
"warningCount": 0,
"warnings": Array [],
},
]
}
/>
</div>
`;

+ 26
- 0
server/sonar-web/src/main/js/apps/background-tasks/components/__tests__/__snapshots__/StatPendingCount-test.tsx.snap Ver fichero

@@ -0,0 +1,26 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP

exports[`should render correctly 1`] = `
<span>
<span
className="emphasised-measure"
>
5
</span>
<span
className="little-spacer-left display-inline-flex-center"
>
background_tasks.pending
<ConfirmButton
cancelButtonText="close"
confirmButtonText="background_tasks.cancel_all_tasks.submit"
isDestructive={true}
modalBody="background_tasks.cancel_all_tasks.text"
modalHeader="background_tasks.cancel_all_tasks"
onConfirm={[MockFunction]}
>
<Component />
</ConfirmButton>
</span>
</span>
`;

+ 20
- 0
server/sonar-web/src/main/js/apps/background-tasks/components/__tests__/__snapshots__/StatPendingTime-test.tsx.snap Ver fichero

@@ -0,0 +1,20 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP

exports[`should render correctly 1`] = `
<span>
<span
className="emphasised-measure"
>
15s
</span>
<span
className="little-spacer-left"
>
background_tasks.pending_time
</span>
<HelpTooltip
className="little-spacer-left"
overlay="background_tasks.pending_time.description"
/>
</span>
`;

+ 40
- 0
server/sonar-web/src/main/js/apps/background-tasks/components/__tests__/__snapshots__/StatStillFailing-test.tsx.snap Ver fichero

@@ -0,0 +1,40 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP

exports[`should render correctly 1`] = `
<span>
<ButtonLink
className="emphasised-measure text-baseline"
onClick={[MockFunction]}
>
5
</ButtonLink>
<span
className="little-spacer-left"
>
background_tasks.failures
</span>
<HelpTooltip
className="little-spacer-left"
overlay="background_tasks.failing_count"
/>
</span>
`;

exports[`should render without the filter link 1`] = `
<span>
<span
className="emphasised-measure"
>
0
</span>
<span
className="little-spacer-left"
>
background_tasks.failures
</span>
<HelpTooltip
className="little-spacer-left"
overlay="background_tasks.failing_count"
/>
</span>
`;

+ 33
- 0
server/sonar-web/src/main/js/apps/background-tasks/components/__tests__/__snapshots__/Stats-test.tsx.snap Ver fichero

@@ -0,0 +1,33 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP

exports[`should render correctly 1`] = `
<section
className="big-spacer-top big-spacer-bottom"
>
<Connect(StatPendingCount)
onCancelAllPending={[MockFunction]}
pendingCount={2}
/>
<StatPendingTime
className="huge-spacer-left"
pendingCount={2}
pendingTime={110545}
/>
<StatStillFailing
className="huge-spacer-left"
failingCount={4}
onShowFailing={[MockFunction]}
/>
</section>
`;

exports[`should render correctly for a component 1`] = `
<section
className="big-spacer-top big-spacer-bottom"
>
<Connect(StatPendingCount)
onCancelAllPending={[MockFunction]}
pendingCount={2}
/>
</section>
`;

+ 2
- 0
sonar-core/src/main/resources/org/sonar/l10n/core.properties Ver fichero

@@ -2356,6 +2356,8 @@ background_tasks.show_warnings=Show Warnings
background_tasks.error_message=Error Message
background_tasks.error_stacktrace=Error Details
background_tasks.pending=pending
background_tasks.pending_time=pending time
background_tasks.pending_time.description=Pending time of the oldest background task waiting to be processed.
background_tasks.failures=still failing

background_tasks.number_of_workers=Number of Workers:

Cargando…
Cancelar
Guardar