diff options
-rw-r--r-- | package.json | 2 | ||||
-rw-r--r-- | src/App.css | 14 | ||||
-rw-r--r-- | src/App.js | 55 | ||||
-rw-r--r-- | src/Blocks.js | 36 | ||||
-rw-r--r-- | src/CoverageListing.js | 93 | ||||
-rw-r--r-- | src/Report.js | 59 | ||||
-rw-r--r-- | yarn.lock | 59 |
7 files changed, 277 insertions, 41 deletions
diff --git a/package.json b/package.json index 1cb608c..05c7fd6 100644 --- a/package.json +++ b/package.json | |||
@@ -3,6 +3,7 @@ | |||
3 | "version": "0.1.0", | 3 | "version": "0.1.0", |
4 | "private": true, | 4 | "private": true, |
5 | "dependencies": { | 5 | "dependencies": { |
6 | "jszip": "^3.1.5", | ||
6 | "react": "^16.5.2", | 7 | "react": "^16.5.2", |
7 | "react-dom": "^16.5.2", | 8 | "react-dom": "^16.5.2", |
8 | "react-scripts": "2.0.4", | 9 | "react-scripts": "2.0.4", |
@@ -24,6 +25,7 @@ | |||
24 | "not op_mini all" | 25 | "not op_mini all" |
25 | ], | 26 | ], |
26 | "devDependencies": { | 27 | "devDependencies": { |
28 | "@types/jszip": "^3.1.4", | ||
27 | "@types/xml2js": "^0.4.3" | 29 | "@types/xml2js": "^0.4.3" |
28 | } | 30 | } |
29 | } | 31 | } |
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 @@ | |||
51 | .App li[well-covered="false"] { | 51 | .App li[well-covered="false"] { |
52 | color: red; | 52 | color: red; |
53 | } | 53 | } |
54 | |||
55 | .App .listing { | ||
56 | overflow-x: auto; | ||
57 | padding: 0.5rem 0; | ||
58 | background-color: #FAFAFA; | ||
59 | } | ||
60 | |||
61 | .App .listing li[well-covered] { | ||
62 | font-weight: bold; | ||
63 | } | ||
64 | |||
65 | .App pre { | ||
66 | margin: 0.1rem; | ||
67 | } | ||
@@ -18,9 +18,10 @@ | |||
18 | */ | 18 | */ |
19 | 19 | ||
20 | import React, { Component } from 'react'; | 20 | import React, { Component } from 'react'; |
21 | import {Parser} from 'xml2js'; | 21 | import { Parser } from 'xml2js'; |
22 | import JSZip from 'jszip'; | ||
22 | 23 | ||
23 | import {Counters, SessionInfo, PackagesCoverage} from './Blocks.js'; | 24 | import { Report } from './Report.js'; |
24 | import './App.css'; | 25 | import './App.css'; |
25 | 26 | ||
26 | class App extends Component { | 27 | class App extends Component { |
@@ -28,24 +29,31 @@ class App extends Component { | |||
28 | super(props); | 29 | super(props); |
29 | this.state = { | 30 | this.state = { |
30 | report: null, | 31 | report: null, |
32 | sourceSet: null, | ||
31 | hasError: false | 33 | hasError: false |
32 | }; | 34 | }; |
33 | } | 35 | } |
34 | 36 | ||
35 | componentDidCatch(error, info) { | 37 | componentDidCatch(error, info) { |
36 | this.setState({ hasError: true }); | 38 | this.setState({ hasError: true }); |
37 | console.err(error, info); | 39 | console.error(error, info); |
38 | } | 40 | } |
39 | 41 | ||
40 | _useReportFile(file) { | 42 | _useReportFile(file) { |
41 | const fileReader = new FileReader(); | 43 | const fileReader = new FileReader(); |
42 | fileReader.onloadend = (readEvent) => this._useReport(readEvent.target.result); | 44 | fileReader.onloadend = readEvent => this._useReport(readEvent.target.result); |
43 | fileReader.readAsText(file); | 45 | fileReader.readAsText(file); |
44 | } | 46 | } |
45 | 47 | ||
46 | _useReport(xmlString) { | 48 | _useReport(xmlString) { |
49 | this.setState({ hasError: false }); | ||
47 | const xmlParser = new Parser(); | 50 | const xmlParser = new Parser(); |
48 | xmlParser.parseString(xmlString, (err, result) => this.setState({ report: result.report })); | 51 | xmlParser.parseString(xmlString, (_, result) => this.setState({ report: result.report })); |
52 | } | ||
53 | |||
54 | _useSourceArchive(file) { | ||
55 | this.setState({ hasError: false }); | ||
56 | JSZip.loadAsync(file).then(zip => this.setState({ sourceSet: zip.files })); | ||
49 | } | 57 | } |
50 | 58 | ||
51 | _renderError() { | 59 | _renderError() { |
@@ -58,7 +66,7 @@ class App extends Component { | |||
58 | } | 66 | } |
59 | 67 | ||
60 | _renderReport() { | 68 | _renderReport() { |
61 | return this.state.hasError ? this._renderError(): (<Report report={this.state.report} />); | 69 | return this.state.hasError ? this._renderError(): (<Report report={this.state.report} sourceSet={this.state.sourceSet} />); |
62 | } | 70 | } |
63 | 71 | ||
64 | render() { | 72 | render() { |
@@ -73,7 +81,7 @@ class App extends Component { | |||
73 | 81 | ||
74 | <div> | 82 | <div> |
75 | <label htmlFor="sourceJar">Source JAR: </label> | 83 | <label htmlFor="sourceJar">Source JAR: </label> |
76 | <input id="sourceJar" type="file" accept=".jar" onChange={event => null} /> | 84 | <input id="sourceJar" type="file" accept=".jar" onChange={event => this._useSourceArchive(event.target.files[0])} /> |
77 | </div> | 85 | </div> |
78 | 86 | ||
79 | <hr /> | 87 | <hr /> |
@@ -83,37 +91,4 @@ class App extends Component { | |||
83 | } | 91 | } |
84 | } | 92 | } |
85 | 93 | ||
86 | class Report extends Component { | ||
87 | _renderNone() { | ||
88 | return (<span>Please provide a JaCoCo XML report file to visualise.</span>); | ||
89 | } | ||
90 | |||
91 | _renderReport() { | ||
92 | return ( | ||
93 | <div> | ||
94 | <h2>Viewing report: "{this.props.report.$.name}"</h2> | ||
95 | |||
96 | <section> | ||
97 | <h3>Session info</h3> | ||
98 | <SessionInfo data={this.props.report.sessioninfo} /> | ||
99 | </section> | ||
100 | |||
101 | <section> | ||
102 | <h3>Global coverage</h3> | ||
103 | <Counters data={this.props.report.counter} /> | ||
104 | </section> | ||
105 | |||
106 | <section> | ||
107 | <h3>Details</h3> | ||
108 | <PackagesCoverage packages={this.props.report.package} /> | ||
109 | </section> | ||
110 | </div> | ||
111 | ); | ||
112 | } | ||
113 | |||
114 | render() { | ||
115 | return this.props.report ? this._renderReport() : this._renderNone(); | ||
116 | } | ||
117 | } | ||
118 | |||
119 | export default App; | 94 | 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 @@ | |||
18 | */ | 18 | */ |
19 | 19 | ||
20 | import React, { Component } from 'react'; | 20 | import React, { Component } from 'react'; |
21 | import { CoverageListing } from './CoverageListing.js'; | ||
21 | 22 | ||
22 | function renderRows(renderRowFunc, rows, inline) { | 23 | function renderRows(renderRowFunc, rows, inline) { |
23 | const renderedRows = rows ? rows.map(renderRowFunc) : (<li>None.</li>); | 24 | const renderedRows = rows ? rows.map(renderRowFunc) : (<li>None.</li>); |
@@ -97,3 +98,38 @@ class MethodsCoverage extends Component { | |||
97 | return renderRows(this._renderRow, this.props.methods, false); | 98 | return renderRows(this._renderRow, this.props.methods, false); |
98 | } | 99 | } |
99 | } | 100 | } |
101 | |||
102 | export class PackagesSourceCoverage extends Component { | ||
103 | _renderRow(row) { | ||
104 | return ( | ||
105 | <li key={row.$.name}> | ||
106 | <span>{row.$.name}</span> | ||
107 | <SourcesCoverage package={row.$.name} | ||
108 | packageSourceFiles={row.sourcefile} | ||
109 | sourceSet={this.props.sourceSet} /> | ||
110 | </li> | ||
111 | ) | ||
112 | } | ||
113 | |||
114 | render() { | ||
115 | return renderRows(row => this._renderRow(row), this.props.packages, false); | ||
116 | } | ||
117 | } | ||
118 | |||
119 | class SourcesCoverage extends Component { | ||
120 | _renderRow(row) { | ||
121 | const fileName = this.props.package + '/' + row.$.name; | ||
122 | return ( | ||
123 | <li key={fileName}> | ||
124 | <span>{fileName}</span> | ||
125 | <CoverageListing fileName={fileName} | ||
126 | sourceSet={this.props.sourceSet} | ||
127 | coverage={row.line} /> | ||
128 | </li> | ||
129 | ) | ||
130 | } | ||
131 | |||
132 | render() { | ||
133 | return renderRows(row => this._renderRow(row), this.props.packageSourceFiles, false); | ||
134 | } | ||
135 | } | ||
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 @@ | |||
1 | /* | ||
2 | * JaCoCo Report Viewer, a web-based coverage report viewer | ||
3 | * Copyright (C) 2018 Pacien TRAN-GIRARD | ||
4 | * Adam NAILI | ||
5 | * | ||
6 | * This program is free software: you can redistribute it and/or modify | ||
7 | * it under the terms of the GNU General Public License as published by | ||
8 | * the Free Software Foundation, either version 3 of the License, or | ||
9 | * (at your option) any later version. | ||
10 | * | ||
11 | * This program is distributed in the hope that it will be useful, | ||
12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
14 | * GNU General Public License for more details. | ||
15 | * | ||
16 | * You should have received a copy of the GNU General Public License | ||
17 | * along with this program. If not, see <https://www.gnu.org/licenses/>. | ||
18 | */ | ||
19 | |||
20 | import React, { Component } from 'react'; | ||
21 | |||
22 | export class CoverageListing extends Component { | ||
23 | constructor(props) { | ||
24 | super(props); | ||
25 | this.state = { | ||
26 | listingContent: null, | ||
27 | coverageMap: null | ||
28 | }; | ||
29 | } | ||
30 | |||
31 | componentDidMount() { | ||
32 | this._updateState(); | ||
33 | } | ||
34 | |||
35 | componentDidUpdate(prevProps) { | ||