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
---
package.json | 2 ++
src/App.css | 14 ++++++++
src/App.js | 55 ++++++++---------------------
src/Blocks.js | 36 +++++++++++++++++++
src/CoverageListing.js | 93 ++++++++++++++++++++++++++++++++++++++++++++++++++
src/Report.js | 59 ++++++++++++++++++++++++++++++++
yarn.lock | 59 +++++++++++++++++++++++++++++++-
7 files changed, 277 insertions(+), 41 deletions(-)
create mode 100644 src/CoverageListing.js
create mode 100644 src/Report.js
diff --git a/package.json b/package.json
index 1cb608c..05c7fd6 100644
--- a/package.json
+++ b/package.json
@@ -3,6 +3,7 @@
"version": "0.1.0",
"private": true,
"dependencies": {
+ "jszip": "^3.1.5",
"react": "^16.5.2",
"react-dom": "^16.5.2",
"react-scripts": "2.0.4",
@@ -24,6 +25,7 @@
"not op_mini all"
],
"devDependencies": {
+ "@types/jszip": "^3.1.4",
"@types/xml2js": "^0.4.3"
}
}
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 {
Source JAR:
- 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}"
-
-
-
-
-
-
-
- );
- }
-
- 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 ();
+ }
+
+ 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}"
+
+
+
+
+
+
+
+
+
+ );
+ }
+
+ render() {
+ return this.props.report ? this._renderReport() : this._renderNone();
+ }
+}
diff --git a/yarn.lock b/yarn.lock
index e3eb3af..438f668 100644
--- a/yarn.lock
+++ b/yarn.lock
@@ -854,6 +854,13 @@
resolved "https://registry.yarnpkg.com/@types/events/-/events-1.2.0.tgz#81a6731ce4df43619e5c8c945383b3e62a89ea86"
integrity sha512-KEIlhXnIutzKwRbQkGWb/I4HFqBuUykAdHgDED6xqwXJfONCjF5VoE0cXEiurh3XauygxzeDzgtXUqvLkxFzzA==
+"@types/jszip@^3.1.4":
+ version "3.1.4"
+ resolved "https://registry.yarnpkg.com/@types/jszip/-/jszip-3.1.4.tgz#9b81e3901a6988e9459ac27abf483e6b892251af"
+ integrity sha512-UaVbz4buRlBEolZYrxqkrGDOypugYlbqGNrUFB4qBaexrLypTH0jyvaF5jolNy5D+5C4kKV1WJ3Yx9cn/JH8oA==
+ dependencies:
+ "@types/node" "*"
+
"@types/node@*":
version "10.11.6"
resolved "https://registry.yarnpkg.com/@types/node/-/node-10.11.6.tgz#ce5690df6cd917a9178439a1013e39a7e565c46e"
@@ -2422,6 +2429,11 @@ core-js@2.5.7, core-js@^2.4.0, core-js@^2.5.0:
resolved "https://registry.yarnpkg.com/core-js/-/core-js-2.5.7.tgz#f972608ff0cead68b841a16a932d0b183791814e"
integrity sha512-RszJCAxg/PP6uzXVXL6BsxSXx/B05oJAQ2vkJRjyjrEcNVycaqOmNb5OTxZPE3xa5gwZduqza6L9JOCenh/Ecw==
+core-js@~2.3.0:
+ version "2.3.0"
+ resolved "https://registry.yarnpkg.com/core-js/-/core-js-2.3.0.tgz#fab83fbb0b2d8dc85fa636c4b9d34c75420c6d65"
+ integrity sha1-+rg/uwstjchfpjbEudNMdUIMbWU=
+
core-util-is@1.0.2, core-util-is@~1.0.0:
version "1.0.2"
resolved "https://registry.yarnpkg.com/core-util-is/-/core-util-is-1.0.2.tgz#b5fd54220aa2bc5ab57aab7140c940754503c1a7"
@@ -3177,6 +3189,11 @@ es-to-primitive@^1.1.1:
is-date-object "^1.0.1"
is-symbol "^1.0.2"
+es6-promise@~3.0.2:
+ version "3.0.2"
+ resolved "https://registry.yarnpkg.com/es6-promise/-/es6-promise-3.0.2.tgz#010d5858423a5f118979665f46486a95c6ee2bb6"
+ integrity sha1-AQ1YWEI6XxGJeWZfRkhqlcbuK7Y=
+
escape-html@~1.0.3:
version "1.0.3"
resolved "https://registry.yarnpkg.com/escape-html/-/escape-html-1.0.3.tgz#0258eae4d3d0c0974de1c169188ef0051d1d1988"
@@ -4472,6 +4489,11 @@ ignore@^4.0.6:
resolved "https://registry.yarnpkg.com/ignore/-/ignore-4.0.6.tgz#750e3db5862087b4737ebac8207ffd1ef27b25fc"
integrity sha512-cyFDKrqc/YdcWFniJhzI42+AzS+gNwmUzOSFcRCQYwySuBBBy/KjuxWLZ/FHEH6Moq1NizMOBWyTcv8O4OZIMg==
+immediate@~3.0.5:
+ version "3.0.6"
+ resolved "https://registry.yarnpkg.com/immediate/-/immediate-3.0.6.tgz#9db1dbd0faf8de6fbe0f5dd5e56bb606280de69b"
+ integrity sha1-nbHb0Pr43m++D13V5Wu2BigN5ps=
+
import-cwd@^2.0.0:
version "2.1.0"
resolved "https://registry.yarnpkg.com/import-cwd/-/import-cwd-2.1.0.tgz#aa6cf36e722761285cb371ec6519f53e2435b0a9"
@@ -5545,6 +5567,17 @@ jsx-ast-utils@^2.0.1:
dependencies:
array-includes "^3.0.3"
+jszip@^3.1.5:
+ version "3.1.5"
+ resolved "https://registry.yarnpkg.com/jszip/-/jszip-3.1.5.tgz#e3c2a6c6d706ac6e603314036d43cd40beefdf37"
+ integrity sha512-5W8NUaFRFRqTOL7ZDDrx5qWHJyBXy6velVudIzQUSoqAAYqzSh2Z7/m0Rf1QbmQJccegD0r+YZxBjzqoBiEeJQ==
+ dependencies:
+ core-js "~2.3.0"
+ es6-promise "~3.0.2"
+ lie "~3.1.0"
+ pako "~1.0.2"
+ readable-stream "~2.0.6"
+
killable@^1.0.0:
version "1.0.1"
resolved "https://registry.yarnpkg.com/killable/-/killable-1.0.1.tgz#4c8ce441187a061c7474fb87ca08e2a638194892"
@@ -5636,6 +5669,13 @@ levn@^0.3.0, levn@~0.3.0:
prelude-ls "~1.1.2"
type-check "~0.3.2"
+lie@~3.1.0:
+ version "3.1.1"
+ resolved "https://registry.yarnpkg.com/lie/-/lie-3.1.1.tgz#9a436b2cc7746ca59de7a41fa469b3efb76bd87e"
+ integrity sha1-mkNrLMd0bKWd56QfpGmz77dr2H4=
+ dependencies:
+ immediate "~3.0.5"
+
load-json-file@^1.0.0:
version "1.1.0"
resolved "https://registry.yarnpkg.com/load-json-file/-/load-json-file-1.1.0.tgz#956905708d58b4bab4c2261b04f59f31c99374c0"
@@ -6574,7 +6614,7 @@ p-try@^2.0.0:
resolved "https://registry.yarnpkg.com/p-try/-/p-try-2.0.0.tgz#85080bb87c64688fa47996fe8f7dfbe8211760b1"
integrity sha512-hMp0onDKIajHfIkdRk3P4CdCmErkYAxxDtP3Wx/4nZ3aGlau2VKh3mZpcuFkH27WQkL/3WBCPOktzA9ZOAnMQQ==
-pako@~1.0.5:
+pako@~1.0.2, pako@~1.0.5:
version "1.0.6"
resolved "https://registry.yarnpkg.com/pako/-/pako-1.0.6.tgz#0101211baa70c4bca4a0f63f2206e97b7dfaf258"
integrity sha512-lQe48YPsMJAig+yngZ87Lus+NF+3mtu7DVOBu6b/gHO1YpKwIj5AWjZ/TOS7i46HD/UixzWb1zeWDZfGZ3iYcg==
@@ -7471,6 +7511,11 @@ private@^0.1.6, private@^0.1.8:
resolved "https://registry.yarnpkg.com/private/-/private-0.1.8.tgz#2381edb3689f7a53d653190060fcf822d2f368ff"
integrity sha512-VvivMrbvd2nKkiG38qjULzlc+4Vx4wm/whI9pQD35YrARNnhxeiRktSOhSukRLFNlzg6Br/cJPet5J/u19r/mg==
+process-nextick-args@~1.0.6:
+ version "1.0.7"
+ resolved "https://registry.yarnpkg.com/process-nextick-args/-/process-nextick-args-1.0.7.tgz#150e20b756590ad3f91093f25a4f2ad8bff30ba3"
+ integrity sha1-FQ4gt1ZZCtP5EJPyWk8q2L/zC6M=
+
process-nextick-args@~2.0.0:
version "2.0.0"
resolved "https://registry.yarnpkg.com/process-nextick-args/-/process-nextick-args-2.0.0.tgz#a37d732f4271b4ab1ad070d35508e8290788ffaa"
@@ -7850,6 +7895,18 @@ readable-stream@1.0:
isarray "0.0.1"
string_decoder "~0.10.x"
+readable-stream@~2.0.6:
+ version "2.0.6"
+ resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-2.0.6.tgz#8f90341e68a53ccc928788dacfcd11b36eb9b78e"
+ integrity sha1-j5A0HmilPMySh4jaz80Rs265t44=
+ dependencies:
+ core-util-is "~1.0.0"
+ inherits "~2.0.1"
+ isarray "~1.0.0"
+ process-nextick-args "~1.0.6"
+ string_decoder "~0.10.x"
+ util-deprecate "~1.0.1"
+
readdirp@^2.0.0:
version "2.2.1"
resolved "https://registry.yarnpkg.com/readdirp/-/readdirp-2.2.1.tgz#0e87622a3325aa33e892285caf8b4e846529a525"
--
cgit v1.2.3