From 9bc43545d438e71a18399ba5d1a5bde57e0ba21f Mon Sep 17 00:00:00 2001 From: pacien Date: Thu, 11 Oct 2018 00:02:59 +0200 Subject: Implement source coverage inspection --- src/App.css | 14 ++++++++ src/App.js | 55 ++++++++--------------------- src/Blocks.js | 36 +++++++++++++++++++ src/CoverageListing.js | 93 ++++++++++++++++++++++++++++++++++++++++++++++++++ src/Report.js | 59 ++++++++++++++++++++++++++++++++ 5 files changed, 217 insertions(+), 40 deletions(-) create mode 100644 src/CoverageListing.js create mode 100644 src/Report.js (limited to 'src') diff --git a/src/App.css b/src/App.css index d0870a2..fc9d5ec 100644 --- a/src/App.css +++ b/src/App.css @@ -51,3 +51,17 @@ .App li[well-covered="false"] { color: red; } + +.App .listing { + overflow-x: auto; + padding: 0.5rem 0; + background-color: #FAFAFA; +} + +.App .listing li[well-covered] { + font-weight: bold; +} + +.App pre { + margin: 0.1rem; +} diff --git a/src/App.js b/src/App.js index 347d243..fb77d93 100644 --- a/src/App.js +++ b/src/App.js @@ -18,9 +18,10 @@ */ import React, { Component } from 'react'; -import {Parser} from 'xml2js'; +import { Parser } from 'xml2js'; +import JSZip from 'jszip'; -import {Counters, SessionInfo, PackagesCoverage} from './Blocks.js'; +import { Report } from './Report.js'; import './App.css'; class App extends Component { @@ -28,24 +29,31 @@ class App extends Component { super(props); this.state = { report: null, + sourceSet: null, hasError: false }; } componentDidCatch(error, info) { this.setState({ hasError: true }); - console.err(error, info); + console.error(error, info); } _useReportFile(file) { const fileReader = new FileReader(); - fileReader.onloadend = (readEvent) => this._useReport(readEvent.target.result); + fileReader.onloadend = readEvent => this._useReport(readEvent.target.result); fileReader.readAsText(file); } _useReport(xmlString) { + this.setState({ hasError: false }); const xmlParser = new Parser(); - xmlParser.parseString(xmlString, (err, result) => this.setState({ report: result.report })); + xmlParser.parseString(xmlString, (_, result) => this.setState({ report: result.report })); + } + + _useSourceArchive(file) { + this.setState({ hasError: false }); + JSZip.loadAsync(file).then(zip => this.setState({ sourceSet: zip.files })); } _renderError() { @@ -58,7 +66,7 @@ class App extends Component { } _renderReport() { - return this.state.hasError ? this._renderError(): (); + return this.state.hasError ? this._renderError(): (); } render() { @@ -73,7 +81,7 @@ class App extends Component {
- null} /> + this._useSourceArchive(event.target.files[0])} />

@@ -83,37 +91,4 @@ class App extends Component { } } -class Report extends Component { - _renderNone() { - return (Please provide a JaCoCo XML report file to visualise.); - } - - _renderReport() { - return ( -
-

Viewing report: "{this.props.report.$.name}"

- -
-

Session info

- -
- -
-

Global coverage

- -
- -
-

Details

- -
-
- ); - } - - render() { - return this.props.report ? this._renderReport() : this._renderNone(); - } -} - export default App; diff --git a/src/Blocks.js b/src/Blocks.js index d9020fa..f830538 100644 --- a/src/Blocks.js +++ b/src/Blocks.js @@ -18,6 +18,7 @@ */ import React, { Component } from 'react'; +import { CoverageListing } from './CoverageListing.js'; function renderRows(renderRowFunc, rows, inline) { const renderedRows = rows ? rows.map(renderRowFunc) : (
  • None.
  • ); @@ -97,3 +98,38 @@ class MethodsCoverage extends Component { return renderRows(this._renderRow, this.props.methods, false); } } + +export class PackagesSourceCoverage extends Component { + _renderRow(row) { + return ( +
  • + {row.$.name} + +
  • + ) + } + + render() { + return renderRows(row => this._renderRow(row), this.props.packages, false); + } +} + +class SourcesCoverage extends Component { + _renderRow(row) { + const fileName = this.props.package + '/' + row.$.name; + return ( +
  • + {fileName} + +
  • + ) + } + + render() { + return renderRows(row => this._renderRow(row), this.props.packageSourceFiles, false); + } +} diff --git a/src/CoverageListing.js b/src/CoverageListing.js new file mode 100644 index 0000000..8b7830c --- /dev/null +++ b/src/CoverageListing.js @@ -0,0 +1,93 @@ +/* + * JaCoCo Report Viewer, a web-based coverage report viewer + * Copyright (C) 2018 Pacien TRAN-GIRARD + * Adam NAILI + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +import React, { Component } from 'react'; + +export class CoverageListing extends Component { + constructor(props) { + super(props); + this.state = { + listingContent: null, + coverageMap: null + }; + } + + componentDidMount() { + this._updateState(); + } + + componentDidUpdate(prevProps) { + if (this.props.fileName === prevProps.fileName && + this.props.sourceSet === prevProps.sourceSet && + this.props.coverage === prevProps.coverage) return; + + this._updateState(); + } + + _updateState() { + if (!this.props.sourceSet || !(this.props.fileName in this.props.sourceSet)) { + this.setState({ listingContent: null }); + return; + } + + this._updateListingContent(); + this._updateCoverageMap(); + } + + _updateCoverageMap() { + const coverageMap = this.props.coverage.reduce((map, line) => (map[parseInt(line.$.nr)] = line.$, map), {}); + this.setState({ coverageMap: coverageMap }); + } + + _updateListingContent() { + this.props.sourceSet[this.props.fileName] + .async('text') + .then(content => this.setState({ listingContent: content })); + } + + _renderNone() { + return (
    No source file provided.
    ); + } + + _nonEmptyString(str) { + return str ? str : ' '; // workaround for empty
     collapsing
    +  }
    +
    +  _renderLine(lineContent, lineNumber) {
    +    if (!(lineNumber in this.state.coverageMap))
    +      return (
  • {lineContent}
  • ); + + const coverage = this.state.coverageMap[lineNumber]; + const wellCovered = parseInt(coverage.mi) === 0 && parseInt(coverage.mb) === 0; + return (
  • {lineContent}
  • ); + } + + _renderListing() { + const lines = this.state.listingContent + .split('\n') + .map(this._nonEmptyString) + .map((line, index) => this._renderLine(line, index + 1)); + + return (
      {lines}
    ); + } + + render() { + return this.state.listingContent ? this._renderListing() : this._renderNone(); + } +} diff --git a/src/Report.js b/src/Report.js new file mode 100644 index 0000000..e79b832 --- /dev/null +++ b/src/Report.js @@ -0,0 +1,59 @@ +/* + * JaCoCo Report Viewer, a web-based coverage report viewer + * Copyright (C) 2018 Pacien TRAN-GIRARD + * Adam NAILI + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +import React, { Component } from 'react'; +import { Counters, SessionInfo, PackagesCoverage, PackagesSourceCoverage } from './Blocks.js'; + +export class Report extends Component { + _renderNone() { + return (Please provide a JaCoCo XML report file to visualise.); + } + + _renderReport() { + return ( +
    +

    Viewing report: "{this.props.report.$.name}"

    + +
    +

    Session info

    + +
    + +
    +

    Global coverage

    + +
    + +
    +

    Coverage tree

    + +
    + +
    +

    Source coverage

    + +
    +
    + ); + } + + render() { + return this.props.report ? this._renderReport() : this._renderNone(); + } +} -- cgit v1.2.3