@@ -5,88 +5,7 @@ const fs = require('fs'),
55 utils = require ( "./utils" ) ,
66 Constants = require ( './constants' ) ,
77 config = require ( "./config" ) ;
8-
9- let templatesDir = path . join ( __dirname , '../' , 'templates' ) ;
10-
11- function loadInlineCss ( ) {
12- return loadFile ( path . join ( templatesDir , 'assets' , 'browserstack-cypress-report.css' ) ) ;
13- }
14-
15- function loadFile ( fileName ) {
16- return fs . readFileSync ( fileName , 'utf8' ) ;
17- }
18-
19- function createBodyBuildHeader ( report_data ) {
20- let projectNameSpan = `<span class='project-name'> ${ report_data . project_name } </span>` ;
21- let buildNameSpan = `<span class='build-name'> ${ report_data . build_name } </span>` ;
22- let buildMeta = `<div class='build-meta'> ${ buildNameSpan } ${ projectNameSpan } </div>` ;
23- let buildLink = `<div class='build-link'> <a href='${ report_data . build_url } ' rel='noreferrer noopener' target='_blank'> View on BrowserStack </a> </div>` ;
24- let buildHeader = `<div class='build-header'> ${ buildMeta } ${ buildLink } </div>` ;
25- return buildHeader ;
26- }
27-
28- function createBodyBuildTable ( report_data ) {
29- let specs = Object . keys ( report_data . rows ) ,
30- specRow = '' ,
31- specSessions = '' ,
32- sessionBlocks = '' ,
33- specData ,
34- specNameSpan ,
35- specPathSpan ,
36- specStats ,
37- specStatsSpan ,
38- specMeta ,
39- sessionStatus ,
40- sessionClass ,
41- sessionStatusIcon ,
42- sessionLink ;
43-
44- specs . forEach ( ( specName ) => {
45- specData = report_data . rows [ specName ] ;
46-
47- specNameSpan = `<span class='spec-name'> ${ specName } </span>` ;
48- specPathSpan = `<span class='spec-path'> ${ specData . path } </span>` ;
49-
50- specStats = buildSpecStats ( specData . meta ) ;
51- specStatsSpan = `<span class='spec-stats ${ specStats . cssClass } '> ${ specStats . label } </span>` ;
52-
53- specMeta = `<div class='spec-meta'> ${ specNameSpan } ${ specPathSpan } ${ specStatsSpan } </div>` ;
54- sessionBlocks = '' ;
55- specData . sessions . forEach ( ( specSession ) => {
56-
57- sessionStatus = specSession . status ;
58- sessionClass = sessionStatus === 'passed' ? 'session-passed' : 'session-failed' ;
59- sessionStatusIcon = sessionStatus === 'passed' ? "✔ " : "✗ " ;
60-
61- sessionLink = `<a href="${ specSession . link } " rel="noreferrer noopener" target="_blank"> ${ sessionStatusIcon } ${ specSession . name } </a>` ;
62-
63- sessionDetail = `<div class="session-detail ${ sessionClass } "> ${ sessionLink } </div>` ;
64- sessionBlocks = `${ sessionBlocks } ${ sessionDetail } ` ;
65- } ) ;
66- specSessions = `<div class='spec-sessions'> ${ sessionBlocks } </div>` ;
67- specRow = `${ specRow } <div class='spec-row'> ${ specMeta } ${ specSessions } </div>` ;
68- } ) ;
69-
70-
71- return `<div class='build-table'> ${ specRow } </div>` ;
72- }
73-
74- function buildSpecStats ( specMeta ) {
75- let failedSpecs = specMeta . failed ,
76- passedSpecs = specMeta . passed ,
77- totalSpecs = specMeta . total ,
78- specStats = { } ;
79-
80- if ( failedSpecs ) {
81- specStats . label = `${ failedSpecs } /${ totalSpecs } FAILED` ;
82- specStats . cssClass = 'spec-stats-failed' ;
83- } else {
84- specStats . label = `${ passedSpecs } /${ totalSpecs } PASSED` ;
85- specStats . cssClass = 'spec-stats-passed' ;
86- }
87-
88- return specStats ;
89- }
8+ const unzipper = require ( 'unzipper' ) ;
909
9110let reportGenerator = ( bsConfig , buildId , args , rawArgs , cb ) => {
9211 let options = {
@@ -127,7 +46,10 @@ let reportGenerator = (bsConfig, buildId, args, rawArgs, cb) => {
12746 if ( resp . statusCode == 299 ) {
12847 messageType = Constants . messageTypes . INFO ;
12948 errorCode = 'api_deprecated' ;
130- if ( ! build ) {
49+ if ( build ) {
50+ message = build . message ;
51+ logger . info ( message ) ;
52+ } else {
13153 message = Constants . userMessages . API_DEPRECATED ;
13254 logger . info ( message ) ;
13355 }
@@ -159,7 +81,7 @@ let reportGenerator = (bsConfig, buildId, args, rawArgs, cb) => {
15981 } else {
16082 messageType = Constants . messageTypes . SUCCESS ;
16183 message = `Report for build: ${ buildId } was successfully created.` ;
162- await renderReportHTML ( build ) ;
84+ await generateCypressBuildReport ( build ) ;
16385 logger . info ( message ) ;
16486 }
16587 utils . sendUsageReport ( bsConfig , args , message , messageType , errorCode , null , rawArgs ) ;
@@ -169,195 +91,54 @@ let reportGenerator = (bsConfig, buildId, args, rawArgs, cb) => {
16991 } ) ;
17092}
17193
172- async function renderReportHTML ( report_data ) {
173- let resultsDir = ' results';
94+ async function generateCypressBuildReport ( report_data ) {
95+ let resultsDir = path . join ( './' , ' results') ;
17496
17597 if ( ! fs . existsSync ( resultsDir ) ) {
17698 fs . mkdirSync ( resultsDir ) ;
17799 }
178-
179- // Writing the JSON used in creating the HTML file.
180- let jsonReportData = await getJsonReportResponse ( report_data . cypress_custom_json_report_url )
181- fs . writeFileSync (
182- `${ resultsDir } /browserstack-cypress-report.json` ,
183- JSON . stringify ( jsonReportData ) ,
184- ( ) => {
185- if ( err ) {
186- return logger . error ( err ) ;
187- }
188- logger . info ( "The JSON file is saved" ) ;
189- }
190- ) ;
191-
192- let htmlReportData = await getHtmlReportResponse ( report_data . cypress_custom_html_report_url )
193- // Writing the HTML file generated from the JSON data.
194- fs . writeFileSync ( `${ resultsDir } /browserstack-cypress-report.html` , htmlReportData , ( ) => {
195- if ( err ) {
196- return logger . error ( err ) ;
197- }
198- logger . info ( "The HTML file was saved!" ) ;
199- } ) ;
100+ await getReportResponse ( resultsDir , 'report.zip' , report_data . cypress_custom_report_url )
200101}
201102
202- function getHtmlReportResponse ( htmlReportUrl ) {
103+ function getReportResponse ( filePath , fileName , reportJsonUrl ) {
104+ let tmpFilePath = path . join ( filePath , fileName ) ;
105+ const writer = fs . createWriteStream ( tmpFilePath ) ;
203106 return new Promise ( async ( resolve , reject ) => {
204- let reportHtmlResponse = null ;
205- request . get ( htmlReportUrl , function ( err , resp , body ) {
206- if ( err ) {
207- logger . error ( 'Failed to download html report' )
208- logger . error ( utils . formatRequest ( err , resp , body ) ) ;
209- reject ( { } ) ;
210- } else {
211- if ( resp . statusCode != 200 ) {
212- logger . error ( `Non 200 response while downloading html report. Response code: ${ resp . statusCode } ` )
213- reject ( { } ) ;
214- } else {
215- try {
216- reportHtmlResponse = body ;
217- console . log ( `roshan1: the getHtmlReportResponse ${ inspect ( reportHtmlResponse ) } ::` ) ;
218- } catch ( err ) {
219- logger . error ( `Report html response parsing failed. Error: ${ inspect ( err ) } ` )
220- reject ( { } ) ;
221- }
222- }
223- }
224- resolve ( reportHtmlResponse ) ;
225- } ) ;
226- } ) ;
227- }
107+ request . get ( reportJsonUrl ) . on ( 'response' , function ( response ) {
228108
229- function getJsonReportResponse ( reportJsonUrl ) {
230- return new Promise ( async ( resolve , reject ) => {
231- let reportJsonResponse = null ;
232- request . get ( reportJsonUrl , function ( err , resp , body ) {
233- if ( err ) {
234- logger . error ( 'Failed to download json report' )
235- logger . error ( utils . formatRequest ( err , resp , body ) ) ;
236- reject ( { } ) ;
109+ if ( response . statusCode != 200 ) {
110+ reject ( ) ;
237111 } else {
238- if ( resp . statusCode != 200 ) {
239- logger . error ( `Non 200 response while downloading json report. Response code: ${ resp . statusCode } ` )
240- reject ( { } ) ;
241- } else {
242- try {
243- reportJsonResponse = JSON . parse ( body ) ;
244- } catch ( err ) {
245- logger . error ( `Report json response parsing failed. Error: ${ inspect ( err ) } ` )
246- reject ( { } ) ;
112+ //ensure that the user can call `then()` only when the file has
113+ //been downloaded entirely.
114+ response . pipe ( writer ) ;
115+ let error = null ;
116+ writer . on ( 'error' , err => {
117+ error = err ;
118+ writer . close ( ) ;
119+ reject ( err ) ;
120+ } ) ;
121+ writer . on ( 'close' , async ( ) => {
122+ if ( ! error ) {
123+ await unzipFile ( filePath , fileName ) ;
124+ fs . unlinkSync ( tmpFilePath ) ;
125+ resolve ( true ) ;
247126 }
248- }
127+ //no need to call the reject here, as it will have been called in the
128+ //'error' stream;
129+ } ) ;
249130 }
250- resolve ( reportJsonResponse ) ;
251- } ) ;
131+ } ) ;
252132 } ) ;
253133}
254134
255- function getResultsJsonResponse ( combination ) {
256- return new Promise ( async ( resolve , reject ) => {
257- resultsJsonResponse = null
258- resultsJsonError = false ;
259- request . get ( combination . tests . result_json , function ( err , resp , body ) {
260- if ( err ) {
261- resultsJsonError = true ;
262- reject ( [ resultsJsonResponse , resultsJsonError ] ) ;
263- } else {
264- if ( resp . statusCode != 200 ) {
265- resultsJsonError = true ;
266- reject ( [ resultsJsonResponse , resultsJsonError ] ) ;
267- } else {
268- try {
269- resultsJsonResponse = JSON . parse ( body ) ;
270- } catch ( err ) {
271- resultsJsonError = true
272- reject ( [ resultsJsonResponse , resultsJsonError ] ) ;
273- }
274- }
275- }
276- resolve ( [ resultsJsonResponse , resultsJsonError ] ) ;
277- } ) ;
135+ const unzipFile = async ( filePath , fileName ) => {
136+ return new Promise ( async ( resolve , reject ) => {
137+ await unzipper . Open . file ( path . join ( filePath , fileName ) )
138+ . then ( d => d . extract ( { path : filePath , concurrency : 5 } ) )
139+ . catch ( ( err ) => reject ( err ) ) ;
140+ resolve ( ) ;
278141 } ) ;
279142}
280143
281- function generateCypressCombinationSpecReportDataWithConfigJson ( combination ) {
282- return new Promise ( async ( resolve , reject ) => {
283- try {
284- let configJsonError , resultsJsonError ;
285- let configJson , resultsJson ;
286-
287- await Promise . all ( [ getConfigJsonResponse ( combination ) , getResultsJsonResponse ( combination ) ] ) . then ( function ( successResult ) {
288- [ [ configJson , configJsonError ] , [ resultsJson , resultsJsonError ] ] = successResult ;
289- } ) . catch ( function ( failureResult ) {
290- [ [ configJson , configJsonError ] , [ resultsJson , resultsJsonError ] ] = failureResult ;
291- } ) ;
292-
293- if ( resultsJsonError || configJsonError ) {
294- resolve ( ) ;
295- }
296- let tests = { } ;
297- if ( utils . isUndefined ( configJson . tests ) || utils . isUndefined ( resultsJson . tests ) ) {
298- resolve ( ) ;
299- }
300- configJson . tests . forEach ( ( test ) => {
301- tests [ test [ "clientId" ] ] = test ;
302- } ) ;
303- resultsJson . tests . forEach ( ( test ) => {
304- tests [ test [ "clientId" ] ] = Object . assign (
305- tests [ test [ "clientId" ] ] ,
306- test
307- ) ;
308- } ) ;
309- let sessionTests = [ ] ;
310- Object . keys ( tests ) . forEach ( ( testId ) => {
311- sessionTests . push ( {
312- name : tests [ testId ] [ "title" ] . pop ( ) ,
313- status : tests [ testId ] [ "state" ] ,
314- duration : parseFloat (
315- tests [ testId ] [ "attempts" ] . pop ( ) [ "wallClockDuration" ] / 1000
316- ) . toFixed ( 2 ) ,
317- } ) ;
318- } ) ;
319- combination . tests = sessionTests ;
320- resolve ( combination . tests ) ;
321- } catch ( error ) {
322- process . exitCode = Constants . ERROR_EXIT_CODE ;
323- reject ( error ) ;
324- }
325- } )
326- }
327-
328- function generateCypressCombinationSpecReportDataWithoutConfigJson ( combination ) {
329- return new Promise ( async ( resolve , reject ) => {
330- try {
331- let resultsJson , resultsJsonError ;
332- await getResultsJsonResponse ( combination ) . then ( function ( successResult ) {
333- [ resultsJson , resultsJsonError ] = successResult
334- } ) . catch ( function ( failureResult ) {
335- [ resultsJson , resultsJsonError ] = failureResult
336- } )
337- if ( resultsJsonError || utils . isUndefined ( resultsJsonResponse ) ) {
338- resolve ( ) ;
339- }
340- let sessionTests = [ ] ;
341- if ( utils . isUndefined ( resultsJson . tests ) ) {
342- resolve ( ) ;
343- }
344- resultsJson . tests . forEach ( ( test ) => {
345- durationKey = utils . isUndefined ( test [ "attempts" ] ) ? test : test [ "attempts" ] . pop ( )
346- sessionTests . push ( {
347- name : test [ "title" ] . pop ( ) ,
348- status : test [ "state" ] ,
349- duration : parseFloat (
350- durationKey [ "wallClockDuration" ] / 1000
351- ) . toFixed ( 2 )
352- } )
353- } ) ;
354- combination . tests = sessionTests ;
355- resolve ( combination . tests ) ;
356- } catch ( error ) {
357- process . exitCode = Constants . ERROR_EXIT_CODE ;
358- reject ( error ) ;
359- }
360- } )
361- }
362-
363144exports . reportGenerator = reportGenerator ;
0 commit comments