diff --git a/src/containers/fileViewer.jsx b/src/containers/fileViewer.jsx index 1d89bf33..1dfe18b8 100644 --- a/src/containers/fileViewer.jsx +++ b/src/containers/fileViewer.jsx @@ -1,11 +1,16 @@ import React, { Component } from 'react'; -import { fileRevisionCoverageSummary, fileRevisionWithActiveData } from '../utils/coverage'; +import FileOutlineIcon from 'mdi-react/FileOutlineIcon'; +import FolderOutlineIcon from 'mdi-react/FolderOutlineIcon'; +import settings from '../settings'; +import { sourceCoverageSummary, sourceCoverageFromActiveData, pathCoverageFromBackend } from '../utils/coverage'; import { rawFile } from '../utils/hg'; import { TestsSideViewer, CoveragePercentageViewer } from '../components/fileViewer'; import { HORIZONTAL_ELLIPSIS, HEAVY_CHECKMARK } from '../utils/symbol'; import hash from '../utils/hash'; +const { low, medium, high } = settings.COVERAGE_THRESHOLDS; + // FileViewer loads a raw file for a given revision from Mozilla's hg web. // It uses test coverage information from Active Data to show coverage // for runnable lines. @@ -28,7 +33,8 @@ export default class FileViewerContainer extends Component { // Reset the state and fetch new data const newState = { appErr: undefined, - coverage: undefined, + pathCoverage: undefined, + sourceCoverage: undefined, parsedFile: undefined, }; // eslint-disable-next-line react/no-did-update-set-state @@ -45,52 +51,89 @@ export default class FileViewerContainer extends Component { } } - fetchData(repoPath = 'mozilla-central') { + async fetchData(repoPath = 'mozilla-central') { const { revision, path } = this.props; - if (!revision || !path) { - this.setState({ appErr: "Undefined URL query ('revision', 'path' fields are required)" }); + if (!revision) { + this.setState({ appErr: "Undefined URL query (field 'revision' is required)" }); return; } - // Get source code from hg - const fileSource = async () => { - this.setState({ parsedFile: (await rawFile(revision, path, repoPath)) }); + // Get overall path coverage from backend + const pathCoverage = async () => { + const data = await pathCoverageFromBackend(revision, path, repoPath); + this.setState({ pathCoverage: data }); }; - // Get coverage from ActiveData - const coverageData = async () => { - const { data } = await fileRevisionWithActiveData(revision, path, repoPath); - this.setState({ coverage: fileRevisionCoverageSummary(data) }); + // Get detailed source coverage from ActiveData + const fileCoverage = async () => { + const { data } = await sourceCoverageFromActiveData(revision, path, repoPath); + this.setState({ sourceCoverage: sourceCoverageSummary(data) }); + }; + // Get raw source code from hg + const fileSource = async () => { + const parsedFile = await rawFile(revision, path, repoPath); + this.setState({ parsedFile }); }; // Fetch source code and coverage in parallel try { - Promise.all([fileSource(), coverageData()]) - .catch((e) => { - if ((e instanceof RangeError) && (e.message === 'Revision number too short')) { - this.setState({ appErr: 'Revision number is too short. Unable to fetch tests.' }); - } else { - this.setState({ appErr: `${e.name}: ${e.message}` }); - } - throw e; - }); + await Promise.all([pathCoverage(), fileCoverage(), fileSource()]); } catch (error) { - this.setState({ appErr: `${error.name}: ${error.message}` }); + console.error(error); + if ((error instanceof RangeError) && (error.message === 'Revision number too short')) { + this.setState({ appErr: 'Revision number is too short. Unable to fetch data.' }); + } else { + this.setState({ appErr: `${error.name}: ${error.message}` }); + } throw error; } } render() { + const { revision, path } = this.props; const { - parsedFile, coverage, selectedLine, appErr, + pathCoverage, sourceCoverage, parsedFile, selectedLine, appErr, } = this.state; return (
File | +Coverage summary | +
---|---|
+
+ {file.type === 'directory' ? |
+ {coveragePercent}% | +