Collect screenshots from Browsertime

Chrome is easy, Firefox needs to crop the image to the current viewport.
There's also changes to the layout, to make the image fit in better (and
we can do this much better).

See this as a first move for #952 and let us improve.
This commit is contained in:
Peter Hedenskog 2016-06-22 09:44:27 +02:00 committed by Tobias Lidskog
parent 1797255577
commit 42450ec5dd
17 changed files with 192 additions and 15 deletions

View File

@ -7,7 +7,7 @@ let Promise = require('bluebird'),
Promise.promisifyAll(fs);
function shouldIgnoreMessage(message) {
return ['url', 'error', 'summarize'].indexOf(message.type) >= 0;
return ['url', 'error', 'summarize','browsertime.screenshot'].indexOf(message.type) >= 0;
}
module.exports = {

View File

@ -158,7 +158,7 @@ module.exports = {
run.har.log.pages[0].pageTimings._firstPaint = run.timings.firstPaint;
results.har.log.pages[runIndex].pageTimings._firstPaint = run.timings.firstPaint;
}
// Kind of ugly way to add visualMetrics to a run
// it's outside of browserScripts today
// we could instead pass browsertime.visualMetrics maybe
@ -182,6 +182,13 @@ module.exports = {
}));
}
})
.tap((results) => {
if (results.screenshots) {
queue.postMessage(make('browsertime.screenshot', results.screenshots, {
url
}));
}
})
.catch(BrowsertimeError, (e) => {
log.error('%s generated the following error in Browsertime %s', url, e);
queue.postMessage(make('error', e.message, merge({url}, e.extra)));

View File

@ -1387,3 +1387,9 @@ svg.water-fall-chart {
display: table;
clear: both; }
.screenshot {
padding: 4px;
background-color: #fff;
border: 1px solid #ddd;
border-radius: 4px; }

File diff suppressed because one or more lines are too long

View File

@ -15,8 +15,9 @@ const fs = require('fs'),
Promise.promisifyAll(fs);
class HTMLBuilder {
constructor(storageManager) {
constructor(storageManager, options) {
this.storageManager = storageManager;
this.options = options;
this.summaryPages = {};
this.urlPages = {};
this.urlRunPages = {};
@ -133,7 +134,6 @@ class HTMLBuilder {
version: packageInfo.version,
h: helpers
}, locals);
return this.storageManager.writeHtmlForUrl(url, name + '.html', renderer.renderTemplate('url/run', locals));
}

View File

@ -11,7 +11,7 @@ module.exports = {
},
open(context, options) {
this.HTMLBuilder = new HTMLBuilder(context.storageManager);
this.HTMLBuilder = new HTMLBuilder(context.storageManager, options);
this.options = options;
},

View File

@ -0,0 +1,11 @@
//
// Screenshots
// ===============
//
.screenshot {
padding: 4px;
background-color: #fff;
border: 1px solid #ddd;
border-radius: 4px;
}

View File

@ -34,3 +34,4 @@
@import 'components/summarybox';
@import 'components/misc';
@import 'components/waterfall';
@import 'components/screenshot';

View File

@ -1,9 +1,49 @@
extends ./layout.pug
block content
- var d = pageInfo.data
h1 Page summary
h5 #{daurl}
p Tested using #{h.cap(options.browsertime.browser)} #{h.plural(options.browsertime.iterations, 'time')}.
- var profile = options.mobile ? 'mobile' : 'desktop'
p Tested using #{h.cap(options.browsertime.browser)} #{h.plural(options.browsertime.iterations, 'run')} with #{profile} profile.
.row
.one-half.column
table
tr
th Metric
th Value
if d.coach && d.coach.pageSummary
tr
td Performance score:
td #{d.coach.pageSummary.advice.performance.score}
if d.pagexray && d.pagexray.pageSummary
tr
td Total page size:
td #{h.size.format(d.pagexray.pageSummary.transferSize)}
if d.pagexray && d.pagexray.pageSummary
tr
td Requests:
td #{d.pagexray.pageSummary.requests}
if d.browsertime && d.browsertime.pageSummary && d.browsertime.pageSummary.statistics.timings.firstPaint
tr
td First Paint:
td #{d.browsertime.pageSummary.statistics.timings.firstPaint.median}
if d.browsertime && d.browsertime.pageSummary && d.browsertime.pageSummary.visualMetrics.SpeedIndex
tr
td SpeedIndex:
td #{d.browsertime.pageSummary.visualMetrics.SpeedIndex}
else if d.browsertime && d.browsertime.pageSummary
tr
td RUMSpeedIndex:
td #{d.browsertime.pageSummary.statistics.timings.rumSpeedIndex.median}
.one-half.column
if d.browsertime.pageSummary.screenshots
- var width = options.mobile ? 150 : '100%';
a(href='screenshots/0.png')
img.screenshot(src='screenshots/0.png', width=width, alt='Screenshot of run 0')
include ./summaryBox.pug
@ -21,7 +61,6 @@ block content
- run = 0
h3 Statistics from run #{run + 1}
- var d = pageInfo.data
ul
if d.coach && d.coach.pageSummary
li: a(href='#coach') Coach

View File

@ -1,18 +1,56 @@
extends ./layout.pug
block content
- var d = pageInfo.data
- runNumber = Number(runIndex)+1
h1 Run #{runNumber} summary
h5 #{daurl}
p Tested using #{h.cap(options.browsertime.browser)} #{h.plural(options.browsertime.iterations, 'time')}.
- var profile = options.mobile ? 'mobile' : 'desktop'
p Tested using #{h.cap(options.browsertime.browser)} #{h.plural(options.browsertime.iterations, 'run')} with #{profile} profile.
.row
.one-half.column
table
tr
th Metric
th Value
if d.coach && d.coach.run
tr
td Performance score:
td #{d.coach.run.advice.performance.score}
if d.pagexray && d.pagexray.run
tr
td Total page size:
td #{h.size.format(d.pagexray.run.transferSize)}
if d.pagexray && d.pagexray.run
tr
td Requests:
td #{d.pagexray.run.requests}
if d.browsertime && d.browsertime.run && d.browsertime.run.timings.firstPaint
tr
td First Paint:
td #{d.browsertime.run.timings.firstPaint}
if d.browsertime && d.browsertime.run && d.browsertime.run.visualMetrics && d.browsertime.run.visualMetrics.SpeedIndex
tr
td SpeedIndex:
td #{d.browsertime.run.visualMetrics.SpeedIndex}
else if d.browsertime && d.browsertime.run
tr
td RUMSpeedIndex:
td #{d.browsertime.run.timings.rumSpeedIndex}
.one-half.column
//- No good way to detect if we have screenshots or not right now for a run
- screenshotName = 'screenshots/' + runIndex + '.png'
- var width = options.mobile ? 150 : '100%';
a(href=screenshotName)
img.screenshot(src=screenshotName, width=width, alt='Screenshot')
if pageInfo.error
.error= pageInfo.error
else
| Quick links
ul.menu
- d = pageInfo.data
if d.pagexray && d.pagexray.run
li: a(href='#pagexray') Page summary
if d.coach && d.coach.run

View File

@ -1,6 +1,6 @@
if pageInfo.data.browsertime
- var btStatistics = pageInfo.data.browsertime.pageSummary.statistics
h2 Summary
h2 Timings Summary
- timingMetrics = ['firstPaint', 'fullyLoaded', 'rumSpeedIndex']
.responsive
table
@ -33,7 +33,7 @@ if pageInfo.data.browsertime
tr
td(data-title= name) #{name} (visual metric)
td.number(data-title='min') #{visualMetric.min}
td.number(data-title='median') #{visualMetric.median}
td.number(data-title='median') #{visualMetric.median}
td.number(data-title='mean') #{visualMetric.mean}
td.number(data-title='max') #{visualMetric.max}
else

View File

@ -0,0 +1,65 @@
'use strict';
const path = require('path'),
PNGCrop = require('png-crop'),
Promise = require('bluebird');
Promise.promisifyAll(PNGCrop);
function getImagesAndName(images) {
const imagesAndName = [];
let i = 0;
images.forEach(function(image) {
imagesAndName.push({
data: image,
name: i + '.png'
});
i++;
})
return imagesAndName;
}
function storeFirefoxScreenshots(options, url, imagesAndName, storageManager) {
const width = Number(options.browsertime.viewPort.split('x')[0]);
const height = Number(options.browsertime.viewPort.split('x')[1]);
// Firfox screenshots take the full height of the browser window, so Lets crop
return storageManager.createDirForUrl(url, 'screenshots').
then((dirPath) => {
return Promise.map(imagesAndName, function(screenshot) {
return PNGCrop.cropAsync(screenshot.data, path.join(dirPath, screenshot.name), {
width,
height
});
})
})
}
function storeChromeScreenshots(url, imagesAndName, storageManager) {
return storageManager.createDirForUrl(url, 'screenshots').
then((dirPath) => {
return Promise.map(imagesAndName, function(screenshot) {
return storageManager.writeInDir(dirPath, screenshot.name, screenshot.data);
})
})
}
module.exports = {
name() {
return path.basename(__dirname);
},
open(context, options) {
this.storageManager = context.storageManager;
this.options = options;
},
processMessage(message) {
switch (message.type) {
case 'browsertime.screenshot':
if (this.options.browser === 'firefox') {
return storeFirefoxScreenshots(this.options, message.url, getImagesAndName(message.data), this.storageManager);
} else {
return storeChromeScreenshots(message.url, getImagesAndName(message.data), this.storageManager);
}
}
}
};

View File

@ -57,6 +57,11 @@ module.exports = {
coach: true
});
}
if (allInArray(['browsertime', 'screenshot'], pluginNames)) {
options.browsertime = merge({}, options.browsertime, {
screenshot: true
});
}
return pluginNames;
})
.then(() => {

View File

@ -6,7 +6,7 @@ const Promise = require('bluebird'),
Promise.promisifyAll(fs);
const defaultPlugins = new Set(['browsertime', 'coach', 'domains', 'assets', 'html', 'analysisStorer']);
const defaultPlugins = new Set(['browsertime', 'coach', 'domains', 'assets', 'html', 'analysisStorer','screenshot']);
const pluginsDir = path.join(__dirname, '..', 'plugins');

View File

@ -97,6 +97,10 @@ class StorageManager {
.then((dirPath) => write(dirPath, filename, data));
}
writeInDir(dir, filename, data) {
return write(dir, filename, data);
}
writeHtmlForUrl(url, filename, data) {
return write(this.createDirForUrl(url), filename, data);
}

View File

@ -88,6 +88,7 @@
"node-slack": "0.0.7",
"node-uuid": "1.4.7",
"pagexray": "0.9.0",
"png-crop": "0.0.1",
"pug": "^2.0.0-beta2",
"simplecrawler": "0.7.0",
"webcoach": "0.23.0",

View File

@ -1,5 +1,5 @@
module.exports = {
run(context) {
context.log.info('In posttask!!! (with results: ' + JSON.stringify(context.results) + ')');
context.log.info('In posttask!!!');
}
};