import React from 'react'; import PropTypes from 'prop-types'; import { Container, Spinner, Navbar, Nav } from 'reactstrap'; import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'; import camelCase from 'lodash/camelCase'; import { Helmet } from 'react-helmet'; import { Tab, TabList, TabPanel, Tabs } from 'react-tabs'; import faviconBroken from '../img/push-health-broken.png'; import faviconOk from '../img/push-health-ok.png'; import ErrorMessages from '../shared/ErrorMessages'; import PushModel from '../models/push'; import RepositoryModel from '../models/repository'; import StatusProgress from '../shared/StatusProgress'; import { scrollToLine } from '../helpers/utils'; import { resultColorMap, getIcon } from '../helpers/display'; import { createQueryParams, parseQueryParams, updateQueryParams, } from '../helpers/url'; import InputFilter from '../shared/InputFilter'; import TestMetric from './TestMetric'; import JobListMetric from './JobListMetric'; import CommitHistory from './CommitHistory'; export default class Health extends React.PureComponent { constructor(props) { super(props); const params = new URLSearchParams(props.location.search); this.state = { revision: params.get('revision'), repo: params.get('repo'), currentRepo: null, metrics: {}, jobs: null, result: null, failureMessage: null, defaultTabIndex: 0, testGroup: params.get('testGroup') || '', selectedTest: params.get('selectedTest') || '', selectedTaskId: params.get('selectedTaskId') || '', selectedJobName: params.get('selectedJobName') || '', searchStr: params.get('searchStr') || '', regressionsOrderBy: params.get('regressionsOrderBy') || 'count', regressionsGroupBy: params.get('regressionsGroupBy') || 'path', knownIssuesOrderBy: params.get('knownIssuesOrderBy') || 'count', knownIssuesGroupBy: params.get('knownIssuesGroupBy') || 'path', }; } async componentDidMount() { const { repo, testGroup } = this.state; const { location } = this.props; const { metrics: { linting, builds, tests }, } = await this.updatePushHealth(); const params = parseQueryParams(location.search); let defaultTabIndex; if (params.tab !== undefined) { defaultTabIndex = ['linting', 'builds', 'tests'].findIndex( (metric) => metric === params.tab, ); } else if (testGroup) { defaultTabIndex = 2; } else { defaultTabIndex = [linting, builds, tests].findIndex( (metric) => metric.result === 'fail', ); } const repos = await RepositoryModel.getList(); const currentRepo = repos.find((repoObj) => repoObj.name === repo); this.setState({ defaultTabIndex, currentRepo }); // Update the tests every two minutes. this.testTimerId = setInterval(() => this.updatePushHealth(), 120000); this.notificationsId = setInterval(() => { this.props.clearNotification(); }, 4000); } componentWillUnmount() { clearInterval(this.testTimerId); } updateParamsAndState = (stateObj) => { const { location, history } = this.props; const newParams = { ...parseQueryParams(location.search), ...stateObj, }; const queryString = createQueryParams(newParams); updateQueryParams(queryString, history, location); this.setState(stateObj); }; updatePushHealth = async () => { const { repo, revision, status } = this.state; if (status) { const { running, pending, completed } = status; if (completed > 0 && pending === 0 && running === 0) { clearInterval(this.testTimerId); return; } } const { data, failureStatus } = await PushModel.getHealth(repo, revision); const newState = !failureStatus ? data : { failureMessage: data }; this.setState(newState); return newState; }; setExpanded = (metricName, expanded) => { const root = camelCase(metricName); const key = `${root}Expanded`; const { [key]: oldExpanded } = this.state; if (oldExpanded !== expanded) { this.setState({ [key]: expanded, }); } else if (expanded) { scrollToLine(`#${root}Metric`, 0, 0, { behavior: 'smooth', block: 'center', }); } }; filter = (searchStr) => { const { location, history } = this.props; const newParams = { ...parseQueryParams(location.search), searchStr }; if (!searchStr.length) { delete newParams.searchStr; } const queryString = createQueryParams(newParams); updateQueryParams(queryString, history, location); this.setState({ searchStr }); }; render() { const { metrics, result, repo, revision, jobs, failureMessage, status, searchStr, currentRepo, testGroup, selectedTest, defaultTabIndex, selectedTaskId, selectedJobName, regressionsOrderBy, regressionsGroupBy, knownIssuesOrderBy, knownIssuesGroupBy, } = this.state; const { tests, commitHistory, linting, builds } = metrics; const { notify } = this.props; return ( {!!tests && ( )} {`[${ (status && status.testfailed) || 0 } failures] Push Health`} {!!tests && !!currentRepo && (
{commitHistory.details && ( )}
{linting.result !== 'none' && ( Linting )} {builds.result !== 'none' && ( Builds )} {tests.result !== 'none' && ( Tests )}
)} {failureMessage && } {!failureMessage && !tests && (

Gathering health data...

)} ); } } Health.propTypes = { location: PropTypes.shape({}).isRequired, };