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:
parent
1797255577
commit
42450ec5dd
|
|
@ -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 = {
|
||||
|
|
|
|||
|
|
@ -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)));
|
||||
|
|
|
|||
|
|
@ -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
|
|
@ -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));
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
},
|
||||
|
||||
|
|
|
|||
|
|
@ -0,0 +1,11 @@
|
|||
//
|
||||
// Screenshots
|
||||
// ===============
|
||||
//
|
||||
|
||||
.screenshot {
|
||||
padding: 4px;
|
||||
background-color: #fff;
|
||||
border: 1px solid #ddd;
|
||||
border-radius: 4px;
|
||||
}
|
||||
|
|
@ -34,3 +34,4 @@
|
|||
@import 'components/summarybox';
|
||||
@import 'components/misc';
|
||||
@import 'components/waterfall';
|
||||
@import 'components/screenshot';
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
|
@ -57,6 +57,11 @@ module.exports = {
|
|||
coach: true
|
||||
});
|
||||
}
|
||||
if (allInArray(['browsertime', 'screenshot'], pluginNames)) {
|
||||
options.browsertime = merge({}, options.browsertime, {
|
||||
screenshot: true
|
||||
});
|
||||
}
|
||||
return pluginNames;
|
||||
})
|
||||
.then(() => {
|
||||
|
|
|
|||
|
|
@ -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');
|
||||
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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",
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
module.exports = {
|
||||
run(context) {
|
||||
context.log.info('In posttask!!! (with results: ' + JSON.stringify(context.results) + ')');
|
||||
context.log.info('In posttask!!!');
|
||||
}
|
||||
};
|
||||
|
|
|
|||
Loading…
Reference in New Issue