diff --git a/js/report/report.js b/js/report/report.js index 186295d..bdbe57e 100644 --- a/js/report/report.js +++ b/js/report/report.js @@ -22,9 +22,9 @@ import Rectangle from '../../../scenery/js/nodes/Rectangle.js'; import Text from '../../../scenery/js/nodes/Text.js'; import VBox from '../../../scenery/js/nodes/VBox.js'; import Color from '../../../scenery/js/util/Color.js'; +import TextPushButton from '../../../sun/js/buttons/TextPushButton.js'; import Checkbox from '../../../sun/js/Checkbox.js'; import VerticalAquaRadioButtonGroup from '../../../sun/js/VerticalAquaRadioButtonGroup.js'; -import TextPushButton from '../../../sun/js/buttons/TextPushButton.js'; // window.assertions.enableAssert(); @@ -57,7 +57,7 @@ const startupTimestampProperty = new NumberProperty( 0 ); statusProperty.lazyLink( status => console.log( `Status: ${status}` ) ); -(function snapshotStatusLoop() { +( function snapshotStatusLoop() { const req = new XMLHttpRequest(); req.onload = function() { setTimeout( snapshotStatusLoop, 1000 ); @@ -77,7 +77,7 @@ statusProperty.lazyLink( status => console.log( `Status: ${status}` ) ); }; req.open( 'get', options.server + '/aquaserver/status', true ); req.send(); -})(); +} )(); // {Property.} const reportProperty = new Property( { @@ -89,7 +89,7 @@ const reportProperty = new Property( { window.reportProperty = reportProperty; -(function reportLoop() { +( function reportLoop() { const req = new XMLHttpRequest(); req.onload = function() { setTimeout( reportLoop, 20000 ); @@ -105,7 +105,7 @@ window.reportProperty = reportProperty; }; req.open( 'get', options.server + '/aquaserver/report', true ); req.send(); -})(); +} )(); // {Property.>} - Which repos to expand! const expandedReposProperty = new Property( [] ); @@ -289,11 +289,11 @@ const popup = ( triggerNode, message ) => { // Content iframe.contentWindow.document.open(); iframe.contentWindow.document.write( - '' + - '' + - 'CT Info' + - '' + messageHTML + '' + - '' + '' + + '' + + 'CT Info' + + '' + messageHTML + '' + + '' ); iframe.contentWindow.document.close(); @@ -320,319 +320,320 @@ const popup = ( triggerNode, message ) => { popupIframe = iframe; }; -Property.multilink( [ reportProperty, expandedReposProperty, sortProperty, filterStringProperty, showAverageTimeProperty, showWeightsProperty ], ( report, expandedRepos, sort, filterString, showAverageTime, showWeights ) => { - let tests = []; +Property.multilink( [ reportProperty, expandedReposProperty, sortProperty, filterStringProperty, showAverageTimeProperty, showWeightsProperty ], + ( report, expandedRepos, sort, filterString, showAverageTime, showWeights ) => { + let tests = []; - const everythingName = '(everything)'; + const everythingName = '(everything)'; - tests.push( { - names: [ everythingName ], - indices: _.range( 0, report.testNames.length ), - averageTimes: report.testAverageTimes, - weights: report.testWeights - } ); + tests.push( { + names: [ everythingName ], + indices: _.range( 0, report.testNames.length ), + averageTimes: report.testAverageTimes, + weights: report.testWeights + } ); - // scan to determine what tests we are showing - report.testNames.forEach( ( names, index ) => { - if ( !expandedRepos.includes( names[ 0 ] ) ) { - const lastTest = tests[ tests.length - 1 ]; - if ( lastTest && lastTest.names[ 0 ] === names[ 0 ] ) { - lastTest.indices.push( index ); - lastTest.averageTimes.push( report.testAverageTimes[ index ] ); - lastTest.weights.push( report.testWeights[ index ] ); + // scan to determine what tests we are showing + report.testNames.forEach( ( names, index ) => { + if ( !expandedRepos.includes( names[ 0 ] ) ) { + const lastTest = tests[ tests.length - 1 ]; + if ( lastTest && lastTest.names[ 0 ] === names[ 0 ] ) { + lastTest.indices.push( index ); + lastTest.averageTimes.push( report.testAverageTimes[ index ] ); + lastTest.weights.push( report.testWeights[ index ] ); + } + else { + tests.push( { + names: [ names[ 0 ] ], + indices: [ index ], + averageTimes: [ report.testAverageTimes[ index ] ], + weights: [ report.testWeights[ index ] ] + } ); + } } else { tests.push( { - names: [ names[ 0 ] ], + names: names, indices: [ index ], averageTimes: [ report.testAverageTimes[ index ] ], weights: [ report.testWeights[ index ] ] } ); } - } - else { - tests.push( { - names: names, - indices: [ index ], - averageTimes: [ report.testAverageTimes[ index ] ], - weights: [ report.testWeights[ index ] ] - } ); - } - } ); - - // compute summations - tests.forEach( test => { - test.averageTime = _.mean( test.averageTimes.filter( _.identity ) ) || 0; - test.minWeight = _.min( test.weights ) || 0; - test.maxWeight = _.max( test.weights ) || 0; - } ); - - if ( filterString.length ) { - // Spaces separate multiple search terms - filterString.split( ' ' ).forEach( filterPart => { - tests = tests.filter( test => _.some( test.names, name => name.includes( filterPart ) ) ); } ); - } - if ( sort === Sort.IMPORTANCE ) { - tests = _.sortBy( tests, test => { - const failIndex = _.findIndex( report.snapshots, snapshot => _.some( test.indices, index => snapshot.tests[ index ].n ) ); - const passIndex = _.findIndex( report.snapshots, snapshot => _.some( test.indices, index => snapshot.tests[ index ].y ) ); - if ( failIndex >= 0 ) { - return failIndex; - } - else if ( passIndex >= 0 ) { - return passIndex + 1000; - } - else { - return 10000; - } + // compute summations + tests.forEach( test => { + test.averageTime = _.mean( test.averageTimes.filter( _.identity ) ) || 0; + test.minWeight = _.min( test.weights ) || 0; + test.maxWeight = _.max( test.weights ) || 0; } ); - } - else if ( sort === Sort.AVERAGE_TIME ) { - tests = _.sortBy( tests, test => -test.averageTime ); - } - else if ( sort === Sort.WEIGHT ) { - tests = _.sortBy( tests, test => -test.maxWeight ); - } - const testLabels = tests.map( test => { - const label = new Text( test.names.join( ' : ' ), { - font: interfaceFont, - left: 0, - top: 0 - } ); - const background = new Rectangle( 0, 0, 0, 0, { - fill: '#fafafa', - children: [ label ], - cursor: 'pointer' - } ); - if ( test.names[ 0 ] === everythingName ) { - label.fill = '#999'; + if ( filterString.length ) { + // Spaces separate multiple search terms + filterString.split( ' ' ).forEach( filterPart => { + tests = tests.filter( test => _.some( test.names, name => name.includes( filterPart ) ) ); + } ); } - background.addInputListener( new FireListener( { - fire: () => { - const topLevelName = test.names[ 0 ]; - if ( test.names.length > 1 ) { - expandedReposProperty.value = expandedReposProperty.value.filter( name => name !== topLevelName ); + + if ( sort === Sort.IMPORTANCE ) { + tests = _.sortBy( tests, test => { + const failIndex = _.findIndex( report.snapshots, snapshot => _.some( test.indices, index => snapshot.tests[ index ].n ) ); + const passIndex = _.findIndex( report.snapshots, snapshot => _.some( test.indices, index => snapshot.tests[ index ].y ) ); + if ( failIndex >= 0 ) { + return failIndex; + } + else if ( passIndex >= 0 ) { + return passIndex + 1000; } else { - expandedReposProperty.value = _.uniq( [ ...expandedReposProperty.value, topLevelName ] ); + return 10000; } - } - } ) ); - return background; - } ); - - const averageTimeLabels = showAverageTime ? tests.map( test => { + } ); + } + else if ( sort === Sort.AVERAGE_TIME ) { + tests = _.sortBy( tests, test => -test.averageTime ); + } + else if ( sort === Sort.WEIGHT ) { + tests = _.sortBy( tests, test => -test.maxWeight ); + } - const background = new Rectangle( 0, 0, 0, 0, { - fill: '#fafafa' + const testLabels = tests.map( test => { + const label = new Text( test.names.join( ' : ' ), { + font: interfaceFont, + left: 0, + top: 0 + } ); + const background = new Rectangle( 0, 0, 0, 0, { + fill: '#fafafa', + children: [ label ], + cursor: 'pointer' + } ); + if ( test.names[ 0 ] === everythingName ) { + label.fill = '#999'; + } + background.addInputListener( new FireListener( { + fire: () => { + const topLevelName = test.names[ 0 ]; + if ( test.names.length > 1 ) { + expandedReposProperty.value = expandedReposProperty.value.filter( name => name !== topLevelName ); + } + else { + expandedReposProperty.value = _.uniq( [ ...expandedReposProperty.value, topLevelName ] ); + } + } + } ) ); + return background; } ); - if ( test.averageTime ) { - const tenthsOfSeconds = Math.ceil( test.averageTime / 100 ); - const label = new Text( `${Math.floor( tenthsOfSeconds / 10 )}.${tenthsOfSeconds % 10}s`, { - font: new PhetFont( { size: 10 } ), - left: 0, - top: 0, - fill: '#888' + const averageTimeLabels = showAverageTime ? tests.map( test => { + + const background = new Rectangle( 0, 0, 0, 0, { + fill: '#fafafa' } ); - background.addChild( label ); - } - return background; - } ) : null; + if ( test.averageTime ) { + const tenthsOfSeconds = Math.ceil( test.averageTime / 100 ); + const label = new Text( `${Math.floor( tenthsOfSeconds / 10 )}.${tenthsOfSeconds % 10}s`, { + font: new PhetFont( { size: 10 } ), + left: 0, + top: 0, + fill: '#888' + } ); + background.addChild( label ); + } - const weightLabels = showWeights ? tests.map( test => { + return background; + } ) : null; - const background = new Rectangle( 0, 0, 0, 0, { - fill: '#fafafa' - } ); + const weightLabels = showWeights ? tests.map( test => { - if ( test.minWeight || test.maxWeight ) { - const label = new Text( test.minWeight === test.maxWeight ? test.minWeight : `${test.minWeight}-${test.maxWeight}`, { - font: new PhetFont( { size: 10 } ), - left: 0, - top: 0, - fill: '#888' + const background = new Rectangle( 0, 0, 0, 0, { + fill: '#fafafa' } ); - background.addChild( label ); - } - return background; - } ) : null; + if ( test.minWeight || test.maxWeight ) { + const label = new Text( test.minWeight === test.maxWeight ? test.minWeight : `${test.minWeight}-${test.maxWeight}`, { + font: new PhetFont( { size: 10 } ), + left: 0, + top: 0, + fill: '#888' + } ); + background.addChild( label ); + } - const padding = 3; + return background; + } ) : null; - const snapshotLabels = report.snapshots.map( ( snapshot, index ) => { - const label = new VBox( { - spacing: 2, - children: [ - ...new Date( snapshot.timestamp ).toLocaleString().replace( ',', '' ).replace( ' AM', 'am' ).replace( ' PM', 'pm' ).split( ' ' ).map( str => new Text( str, { font: new PhetFont( { size: 10 } ) } ) ) - ], - cursor: 'pointer' - } ); - label.addInputListener( new FireListener( { - fire: () => { - let diffString = ''; - - const previousSnapshot = report.snapshots[ index + 1 ]; - if ( previousSnapshot ) { - diffString = _.uniq( Object.keys( snapshot.shas ).concat( Object.keys( previousSnapshot.shas ) ) ).sort().filter( repo => { - return snapshot.shas[ repo ] !== previousSnapshot.shas[ repo ]; - } ).map( repo => `${repo}: ${previousSnapshot.shas[ repo ]} => ${snapshot.shas[ repo ]}` ).join( '\n' ); - } + const padding = 3; - popup( label, `${snapshot.timestamp}\n\n${diffString}\n\n${JSON.stringify( snapshot.shas, null, 2 )}` ); - } - } ) ); - return label; - } ); + const snapshotLabels = report.snapshots.map( ( snapshot, index ) => { + const label = new VBox( { + spacing: 2, + children: [ + ...new Date( snapshot.timestamp ).toLocaleString().replace( ',', '' ).replace( ' AM', 'am' ).replace( ' PM', 'pm' ).split( ' ' ).map( str => new Text( str, { font: new PhetFont( { size: 10 } ) } ) ) + ], + cursor: 'pointer' + } ); + label.addInputListener( new FireListener( { + fire: () => { + let diffString = ''; + + const previousSnapshot = report.snapshots[ index + 1 ]; + if ( previousSnapshot ) { + diffString = _.uniq( Object.keys( snapshot.shas ).concat( Object.keys( previousSnapshot.shas ) ) ).sort().filter( repo => { + return snapshot.shas[ repo ] !== previousSnapshot.shas[ repo ]; + } ).map( repo => `${repo}: ${previousSnapshot.shas[ repo ]} => ${snapshot.shas[ repo ]}` ).join( '\n' ); + } - const maxTestLabelWidth = _.max( testLabels.map( node => node.width ) ); - const maxTestLabelHeight = _.max( testLabels.map( node => node.height ) ); - const maxSnapshotLabelWidth = _.max( snapshotLabels.map( node => node.width ) ); - const maxSnapshotLabelHeight = _.max( snapshotLabels.map( node => node.height ) ); - const maxAverageTimeLabelWidth = averageTimeLabels ? _.max( averageTimeLabels.map( node => node.width ) ) : 0; - const maxWeightLabelWidth = weightLabels ? _.max( weightLabels.map( node => node.width ) ) : 0; + popup( label, `${snapshot.timestamp}\n\n${diffString}\n\n${JSON.stringify( snapshot.shas, null, 2 )}` ); + } + } ) ); + return label; + } ); - testLabels.forEach( label => { - label.rectWidth = maxTestLabelWidth; - label.rectHeight = maxTestLabelHeight; - } ); - averageTimeLabels && averageTimeLabels.forEach( label => { - if ( label.children[ 0 ] ) { - label.children[ 0 ].right = maxAverageTimeLabelWidth; - label.children[ 0 ].centerY = maxTestLabelHeight / 2; - } - label.rectWidth = maxAverageTimeLabelWidth; - label.rectHeight = maxTestLabelHeight; - } ); - weightLabels && weightLabels.forEach( label => { - if ( label.children[ 0 ] ) { - label.children[ 0 ].right = maxWeightLabelWidth; - label.children[ 0 ].centerY = maxTestLabelHeight / 2; - } - label.rectWidth = maxWeightLabelWidth; - label.rectHeight = maxTestLabelHeight; - } ); + const maxTestLabelWidth = _.max( testLabels.map( node => node.width ) ); + const maxTestLabelHeight = _.max( testLabels.map( node => node.height ) ); + const maxSnapshotLabelWidth = _.max( snapshotLabels.map( node => node.width ) ); + const maxSnapshotLabelHeight = _.max( snapshotLabels.map( node => node.height ) ); + const maxAverageTimeLabelWidth = averageTimeLabels ? _.max( averageTimeLabels.map( node => node.width ) ) : 0; + const maxWeightLabelWidth = weightLabels ? _.max( weightLabels.map( node => node.width ) ) : 0; - const getX = index => maxTestLabelWidth + padding + index * ( maxSnapshotLabelWidth + padding ) + ( showAverageTime ? 1 : 0 ) * ( maxAverageTimeLabelWidth + padding ) + ( showWeights ? 1 : 0 ) * ( maxWeightLabelWidth + padding ); - const getY = index => maxSnapshotLabelHeight + padding + index * ( maxTestLabelHeight + padding ); + testLabels.forEach( label => { + label.rectWidth = maxTestLabelWidth; + label.rectHeight = maxTestLabelHeight; + } ); + averageTimeLabels && averageTimeLabels.forEach( label => { + if ( label.children[ 0 ] ) { + label.children[ 0 ].right = maxAverageTimeLabelWidth; + label.children[ 0 ].centerY = maxTestLabelHeight / 2; + } + label.rectWidth = maxAverageTimeLabelWidth; + label.rectHeight = maxTestLabelHeight; + } ); + weightLabels && weightLabels.forEach( label => { + if ( label.children[ 0 ] ) { + label.children[ 0 ].right = maxWeightLabelWidth; + label.children[ 0 ].centerY = maxTestLabelHeight / 2; + } + label.rectWidth = maxWeightLabelWidth; + label.rectHeight = maxTestLabelHeight; + } ); - const snapshotsTestNodes = _.flatten( report.snapshots.map( ( snapshot, i ) => { - return tests.map( ( test, j ) => { - const x = getX( i ); - const y = getY( j ); - const background = new Rectangle( 0, 0, maxSnapshotLabelWidth, maxTestLabelHeight, { - x: x, - y: y - } ); + const getX = index => maxTestLabelWidth + padding + index * ( maxSnapshotLabelWidth + padding ) + ( showAverageTime ? 1 : 0 ) * ( maxAverageTimeLabelWidth + padding ) + ( showWeights ? 1 : 0 ) * ( maxWeightLabelWidth + padding ); + const getY = index => maxSnapshotLabelHeight + padding + index * ( maxTestLabelHeight + padding ); - let totalCount = 0; - let untestedCount = 0; - let unavailableCount = 0; - let passCount = 0; - let failCount = 0; - let messages = []; + const snapshotsTestNodes = _.flatten( report.snapshots.map( ( snapshot, i ) => { + return tests.map( ( test, j ) => { + const x = getX( i ); + const y = getY( j ); + const background = new Rectangle( 0, 0, maxSnapshotLabelWidth, maxTestLabelHeight, { + x: x, + y: y + } ); - test.indices.forEach( index => { - totalCount++; + let totalCount = 0; + let untestedCount = 0; + let unavailableCount = 0; + let passCount = 0; + let failCount = 0; + let messages = []; + + test.indices.forEach( index => { + totalCount++; + + const snapshotTest = snapshot.tests[ index ]; + + if ( typeof snapshotTest.y === 'number' ) { + passCount += snapshotTest.y; + failCount += snapshotTest.n; + if ( snapshotTest.y + snapshotTest.n === 0 ) { + untestedCount++; + } + if ( snapshotTest.m ) { + messages = messages.concat( snapshotTest.m.map( message => { + let resultMessage = `${report.testNames[ index ].join( ' : ' )}\n${message}\nSnapshot from ${new Date( snapshot.timestamp ).toLocaleString()}`; + while ( resultMessage.includes( '\n\n\n' ) ) { + resultMessage = resultMessage.replace( '\n\n\n', '\n\n' ); + } + return resultMessage; + } ) ); + } + } + else { + untestedCount++; + unavailableCount++; + } + } ); - const snapshotTest = snapshot.tests[ index ]; + const completeRatio = totalCount ? ( ( totalCount - untestedCount ) / totalCount ) : 1; - if ( typeof snapshotTest.y === 'number' ) { - passCount += snapshotTest.y; - failCount += snapshotTest.n; - if ( snapshotTest.y + snapshotTest.n === 0 ) { - untestedCount++; + if ( failCount > 0 ) { + if ( untestedCount === 0 ) { + background.fill = failColor; } - if ( snapshotTest.m ) { - messages = messages.concat( snapshotTest.m.map( message => { - let resultMessage = `${report.testNames[ index ].join( ' : ' )}\n${message}\nSnapshot from ${new Date( snapshot.timestamp ).toLocaleString()}`; - while ( resultMessage.includes( '\n\n\n' ) ) { - resultMessage = resultMessage.replace( '\n\n\n', '\n\n' ); - } - return resultMessage; + else { + background.fill = failColorPartial; + background.addChild( new Rectangle( 0, 0, completeRatio * maxSnapshotLabelWidth, maxTestLabelHeight, { + fill: failColor } ) ); } } - else { - untestedCount++; - unavailableCount++; + else if ( passCount > 0 ) { + if ( untestedCount === 0 ) { + background.fill = passColor; + } + else { + background.fill = passColorPartial; + background.addChild( new Rectangle( 0, 0, completeRatio * maxSnapshotLabelWidth, maxTestLabelHeight, { + fill: passColor + } ) ); + } } - } ); - - const completeRatio = totalCount ? ( ( totalCount - untestedCount ) / totalCount ) : 1; - - if ( failCount > 0 ) { - if ( untestedCount === 0 ) { - background.fill = failColor; + else if ( unavailableCount > 0 ) { + background.fill = unavailableColor; } else { - background.fill = failColorPartial; - background.addChild( new Rectangle( 0, 0, completeRatio * maxSnapshotLabelWidth, maxTestLabelHeight, { - fill: failColor - } ) ); + background.fill = untestedColor; } - } - else if ( passCount > 0 ) { - if ( untestedCount === 0 ) { - background.fill = passColor; - } - else { - background.fill = passColorPartial; - background.addChild( new Rectangle( 0, 0, completeRatio * maxSnapshotLabelWidth, maxTestLabelHeight, { - fill: passColor + + if ( messages.length ) { + background.addInputListener( new FireListener( { + fire: () => { + popup( background, messages.join( '\n\n----------------------------------\n\n' ) ); + } } ) ); + background.cursor = 'pointer'; } - } - else if ( unavailableCount > 0 ) { - background.fill = unavailableColor; - } - else { - background.fill = untestedColor; - } - if ( messages.length ) { - background.addInputListener( new FireListener( { - fire: () => { - popup( background, messages.join( '\n\n----------------------------------\n\n' ) ); - } - } ) ); - background.cursor = 'pointer'; - } + return background; + } ); + } ) ); - return background; + testLabels.forEach( ( label, i ) => { + label.left = 0; + label.top = getY( i ); + } ); + snapshotLabels.forEach( ( label, i ) => { + label.top = 0; + label.left = getX( i ); + } ); + averageTimeLabels && averageTimeLabels.forEach( ( label, i ) => { + label.left = maxTestLabelWidth + padding; + label.top = getY( i ); + } ); + weightLabels && weightLabels.forEach( ( label, i ) => { + label.left = maxTestLabelWidth + padding + ( showAverageTime ? 1 : 0 ) * ( maxAverageTimeLabelWidth + padding ); + label.top = getY( i ); } ); - } ) ); - testLabels.forEach( ( label, i ) => { - label.left = 0; - label.top = getY( i ); - } ); - snapshotLabels.forEach( ( label, i ) => { - label.top = 0; - label.left = getX( i ); - } ); - averageTimeLabels && averageTimeLabels.forEach( ( label, i ) => { - label.left = maxTestLabelWidth + padding; - label.top = getY( i ); + reportNode.children = [ + ...testLabels, + ...snapshotLabels, + ...snapshotsTestNodes, + ...( showAverageTime ? averageTimeLabels : [] ), + ...( showWeights ? weightLabels : [] ) + ]; } ); - weightLabels && weightLabels.forEach( ( label, i ) => { - label.left = maxTestLabelWidth + padding + ( showAverageTime ? 1 : 0 ) * ( maxAverageTimeLabelWidth + padding ); - label.top = getY( i ); - } ); - - reportNode.children = [ - ...testLabels, - ...snapshotLabels, - ...snapshotsTestNodes, - ...( showAverageTime ? averageTimeLabels : [] ), - ...( showWeights ? weightLabels : [] ) - ]; -} ); display.initializeEvents(); display.updateOnRequestAnimationFrame( dt => {