1
1
import React , { Component } from 'react' ;
2
2
3
- import { fileRevisionCoverageSummary , fileRevisionWithActiveData } from '../utils/coverage' ;
3
+ import FileOutlineIcon from 'mdi-react/FileOutlineIcon' ;
4
+ import FolderOutlineIcon from 'mdi-react/FolderOutlineIcon' ;
5
+ import settings from '../settings' ;
6
+ import { sourceCoverageSummary , sourceCoverageFromActiveData , pathCoverageFromBackend } from '../utils/coverage' ;
4
7
import { rawFile } from '../utils/hg' ;
5
8
import { TestsSideViewer , CoveragePercentageViewer } from '../components/fileViewer' ;
6
9
import { HORIZONTAL_ELLIPSIS , HEAVY_CHECKMARK } from '../utils/symbol' ;
7
10
import hash from '../utils/hash' ;
8
11
12
+ const { low, medium, high } = settings . COVERAGE_THRESHOLDS ;
13
+
9
14
// FileViewer loads a raw file for a given revision from Mozilla's hg web.
10
15
// It uses test coverage information from Active Data to show coverage
11
16
// for runnable lines.
@@ -28,7 +33,8 @@ export default class FileViewerContainer extends Component {
28
33
// Reset the state and fetch new data
29
34
const newState = {
30
35
appErr : undefined ,
31
- coverage : undefined ,
36
+ pathCoverage : undefined ,
37
+ sourceCoverage : undefined ,
32
38
parsedFile : undefined ,
33
39
} ;
34
40
// eslint-disable-next-line react/no-did-update-set-state
@@ -45,52 +51,112 @@ export default class FileViewerContainer extends Component {
45
51
}
46
52
}
47
53
48
- fetchData ( repoPath = 'mozilla-central' ) {
54
+ async fetchData ( repoPath = 'mozilla-central' ) {
49
55
const { revision, path } = this . props ;
50
- if ( ! revision || ! path ) {
51
- this . setState ( { appErr : "Undefined URL query ('revision', 'path' fields are required)" } ) ;
56
+ if ( ! revision ) {
57
+ this . setState ( { appErr : "Undefined URL query (field 'revision' is required)" } ) ;
52
58
return ;
53
59
}
54
- // Get source code from hg
55
- const fileSource = async ( ) => {
56
- this . setState ( { parsedFile : ( await rawFile ( revision , path , repoPath ) ) } ) ;
60
+ // Get overall path coverage from backend
61
+ const pathCoverage = async ( ) => {
62
+ const data = await pathCoverageFromBackend ( revision , path , repoPath ) ;
63
+ /*
64
+ http://localhost:5000/#/file?revision=ff0d31843793&path=media/
65
+ {
66
+ "children": [
67
+ {
68
+ "coverage": 0.06595940668768085,
69
+ "nb": 42,
70
+ "path": "media/",
71
+ "type": "directory"
72
+ }
73
+ ],
74
+ "type": "directory"
75
+ }
76
+
77
+ http://localhost:5000/#/file?revision=ff0d31843793&path=media/webrtc/trunk/webrtc/base/java/src/org/webrtc/ThreadUtils.java
78
+ {
79
+ "coverage": 0.09302325581395347,
80
+ "nb": 7,
81
+ "path": "media/webrtc/trunk/webrtc/base/java/src/org/webrtc/ThreadUtils.java",
82
+ "type": "file"
83
+ }
84
+ */
85
+ console . log ( 'backend coverage' , data ) ;
86
+ this . setState ( { pathCoverage : data } ) ;
57
87
} ;
58
- // Get coverage from ActiveData
59
- const coverageData = async ( ) => {
60
- const { data } = await fileRevisionWithActiveData ( revision , path , repoPath ) ;
61
- this . setState ( { coverage : fileRevisionCoverageSummary ( data ) } ) ;
88
+ // Get detailed source coverage from ActiveData
89
+ const fileCoverage = async ( ) => {
90
+ const { data } = await sourceCoverageFromActiveData ( revision , path , repoPath ) ;
91
+ this . setState ( { sourceCoverage : sourceCoverageSummary ( data ) } ) ;
92
+ } ;
93
+ // Get raw source code from hg
94
+ const fileSource = async ( ) => {
95
+ const parsedFile = await rawFile ( revision , path , repoPath ) ;
96
+ this . setState ( { parsedFile } ) ;
62
97
} ;
63
98
// Fetch source code and coverage in parallel
64
99
try {
65
- Promise . all ( [ fileSource ( ) , coverageData ( ) ] )
66
- . catch ( ( e ) => {
67
- if ( ( e instanceof RangeError ) && ( e . message === 'Revision number too short' ) ) {
68
- this . setState ( { appErr : 'Revision number is too short. Unable to fetch tests.' } ) ;
69
- } else {
70
- this . setState ( { appErr : `${ e . name } : ${ e . message } ` } ) ;
71
- }
72
- throw e ;
73
- } ) ;
100
+ await Promise . all ( [ pathCoverage ( ) , fileCoverage ( ) , fileSource ( ) ] ) ;
74
101
} catch ( error ) {
75
- this . setState ( { appErr : `${ error . name } : ${ error . message } ` } ) ;
102
+ console . error ( error ) ;
103
+ if ( ( error instanceof RangeError ) && ( error . message === 'Revision number too short' ) ) {
104
+ this . setState ( { appErr : 'Revision number is too short. Unable to fetch data.' } ) ;
105
+ } else {
106
+ this . setState ( { appErr : `${ error . name } : ${ error . message } ` } ) ;
107
+ }
76
108
throw error ;
77
109
}
78
110
}
79
111
80
112
render ( ) {
113
+ const { revision, path } = this . props ;
81
114
const {
82
- parsedFile , coverage , selectedLine, appErr,
115
+ pathCoverage , sourceCoverage , parsedFile , selectedLine, appErr,
83
116
} = this . state ;
84
117
85
118
return (
86
119
< div >
87
120
< div className = "file-view" >
88
121
< FileViewerMeta { ...this . props } { ...this . state } />
89
- { ! appErr && ( parsedFile ) &&
122
+ { pathCoverage && pathCoverage . type === 'directory' &&
123
+ < table className = "changeset-viewer" >
124
+ < tbody >
125
+ < tr >
126
+ < th > File</ th >
127
+ < th > Coverage summary</ th >
128
+ </ tr >
129
+ { pathCoverage . children . map ( ( file ) => {
130
+ const fileName = file . path . replace ( new RegExp ( `^${ path } ` , 'g' ) , '' ) ;
131
+ const coveragePercent = Math . round ( 100 * file . coverage ) ;
132
+ let summaryClassName = high . className ;
133
+ if ( coveragePercent < medium . threshold ) {
134
+ summaryClassName =
135
+ ( coveragePercent < low . threshold ? low . className : medium . className ) ;
136
+ }
137
+ const href =
138
+ `/#/file?revision=${ revision } &path=${ path } ${ fileName } ` ;
139
+ return (
140
+ < tr className = "changeset" key = { fileName } >
141
+ < td className = "changeset-author" >
142
+ < a href = { href } >
143
+ { file . type === 'directory' ? < FolderOutlineIcon /> : < FileOutlineIcon /> }
144
+ < span className = "changeset-eIcon-align" > { fileName } </ span >
145
+ </ a >
146
+ </ td >
147
+ < td className = { `changeset-summary ${ summaryClassName } ` } > { coveragePercent } %</ td >
148
+ </ tr >
149
+ ) ;
150
+ } ) }
151
+ </ tbody >
152
+ </ table > }
153
+ { pathCoverage && pathCoverage . type === 'file' &&
154
+ < div style = { { textAlign : 'center' } } > Coverage: { Math . round ( pathCoverage . coverage * 100 ) } %</ div > }
155
+ { parsedFile &&
90
156
< FileViewer { ...this . state } onLineClick = { this . setSelectedLine } /> }
91
157
</ div >
92
158
< TestsSideViewer
93
- coverage = { coverage }
159
+ coverage = { sourceCoverage }
94
160
lineNumber = { selectedLine }
95
161
/>
96
162
</ div >
@@ -100,7 +166,7 @@ export default class FileViewerContainer extends Component {
100
166
101
167
// This component renders each line of the file with its line number
102
168
const FileViewer = ( {
103
- parsedFile, coverage , selectedLine, onLineClick,
169
+ parsedFile, sourceCoverage , selectedLine, onLineClick,
104
170
} ) => (
105
171
< table className = "file-view-table" >
106
172
< tbody >
@@ -111,7 +177,7 @@ const FileViewer = ({
111
177
key = { uniqueId }
112
178
lineNumber = { lineNumber + 1 }
113
179
text = { text }
114
- coverage = { coverage }
180
+ coverage = { sourceCoverage }
115
181
selectedLine = { selectedLine }
116
182
onLineClick = { onLineClick }
117
183
/>
@@ -156,7 +222,7 @@ const Line = ({
156
222
157
223
// This component contains metadata of the file
158
224
const FileViewerMeta = ( {
159
- revision, path, appErr, parsedFile, coverage ,
225
+ revision, path, appErr, parsedFile, sourceCoverage ,
160
226
} ) => {
161
227
const showStatus = ( label , data ) => (
162
228
< li className = "file-meta-li" >
@@ -168,11 +234,11 @@ const FileViewerMeta = ({
168
234
< div >
169
235
< div className = "file-meta-center" >
170
236
< div className = "file-meta-title" > File Coverage</ div >
171
- { ( coverage ) && < CoveragePercentageViewer coverage = { coverage } /> }
237
+ { ( sourceCoverage ) && < CoveragePercentageViewer coverage = { sourceCoverage } /> }
172
238
< div className = "file-meta-status" >
173
239
< ul className = "file-meta-ul" >
174
240
{ showStatus ( 'Source code' , parsedFile ) }
175
- { showStatus ( 'Coverage' , coverage ) }
241
+ { showStatus ( 'Coverage' , sourceCoverage ) }
176
242
</ ul >
177
243
</ div >
178
244
</ div >
0 commit comments