Show the filmstrip #1555 (#2274)

Inspired by [Stefan Burnickis](https://github.com/sburnicki) work on https://github.com/iteratec/wpt-filmstrip
This commit is contained in:
Peter Hedenskog 2019-01-31 13:44:50 +01:00 committed by GitHub
parent f5ce6c7851
commit 38b743e221
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
13 changed files with 323 additions and 3 deletions

View File

@ -267,6 +267,34 @@ module.exports.parseCommandLine = function parseCommandLine() {
describe: 'Add timer and metrics to the video',
group: 'Browser'
})
.option('browsertime.videoParams.filmstripFullSize', {
alias: 'videoParams.filmstripFullSize',
type: 'boolean',
default: false,
describe:
'Keep original sized screenshots in the filmstrip. Will make the run take longer time',
group: 'Filmstrip'
})
.option('browsertime.videoParams.filmstripQuality', {
alias: 'videoParams.filmstripQuality',
default: 75,
describe: 'The quality of the filmstrip screenshots. 0-100.',
group: 'Filmstrip'
})
.option('browsertime.videoParams.createFilmstrip', {
alias: 'videoParams.createFilmstrip',
type: 'boolean',
default: true,
describe: 'Create filmstrip screenshots.',
group: 'Filmstrip'
})
.option('filmstrip.showAll', {
type: 'boolean',
default: false,
describe:
'Show all screenshots in the filmstrip, independent if they have changed or not.',
group: 'Filmstrip'
})
.option('browsertime.userTimingWhitelist', {
alias: 'userTimingWhitelist',
describe:

View File

@ -41,6 +41,9 @@ module.exports = function storageManager(baseDir, storagePathPrefix, useHash) {
getBaseDir() {
return baseDir;
},
getFullPathToURLDir(url) {
return path.join(baseDir, pathToFolder(url, useHash));
},
getStoragePrefix() {
return storagePathPrefix;
},

File diff suppressed because one or more lines are too long

View File

@ -0,0 +1,158 @@
'use strict';
const fs = require('fs');
const path = require('path');
function findFrame(videoFrames, time) {
let frame = videoFrames[0];
for (let currentFrame of videoFrames) {
if (time >= currentFrame.time) {
frame = currentFrame;
} else {
break;
}
}
return frame;
}
function getMetricsFromBrowsertime(data) {
const metrics = [];
if (data.timings.userTimings && data.timings.userTimings.marks) {
// Some web sites over loads user timings
// So use only the first ones
const maxUserTimings = 10;
let userTimings = 1;
for (let mark of data.timings.userTimings.marks) {
metrics.push({
metric: mark.name,
value: mark.startTime.toFixed()
});
userTimings++;
if (userTimings > maxUserTimings) {
break;
}
}
}
metrics.push({
metric: 'fullyLoaded',
name: 'Fully Loaded',
value: data.fullyLoaded
});
if (data.timings.pageTimings) {
metrics.push({
metric: 'domContentLoadedTime',
name: 'DOM Content Loaded Time',
value: data.timings.pageTimings.domContentLoadedTime
});
metrics.push({
metric: 'pageLoadTime',
name: 'Page Load Time',
value: data.timings.pageTimings.pageLoadTime
});
}
if (data.visualMetrics) {
metrics.push({
metric: 'FirstVisualChange',
name: 'First Visual Change',
value: data.visualMetrics.FirstVisualChange
});
metrics.push({
metric: 'LastVisualChange',
name: 'Last Visual Change',
value: data.visualMetrics.LastVisualChange
});
metrics.push({
metric: 'VisualComplete85',
name: 'Visual Complete 85%',
value: data.visualMetrics.VisualComplete85
});
metrics.push({
metric: 'VisualComplete95',
name: 'Visual Complete 95%',
value: data.visualMetrics.VisualComplete95
});
metrics.push({
metric: 'VisualComplete99',
name: 'Visual Complete 99%',
value: data.visualMetrics.VisualComplete99
});
if (data.visualMetrics.LargestImage) {
metrics.push({
metric: 'LargestImage',
name: 'Largest Image',
value: data.visualMetrics.LargestImage
});
}
if (data.visualMetrics.Logo) {
metrics.push({
metric: 'Logo',
name: 'Logo',
value: data.visualMetrics.Logo
});
}
if (data.visualMetrics.Heading) {
metrics.push({
metric: 'Heading',
name: 'Heading',
value: data.visualMetrics.Heading
});
}
}
return metrics.sort((a, b) => a.value - b.value);
}
function findTimings(timings, start, end) {
return timings.filter(timing => timing.value > start && timing.value <= end);
}
module.exports = {
getFilmstrip(browsertimeData, run, dir, options) {
if (
!options.browsertime.video ||
options.browsertime.videoParams.createFilmstrip === false
) {
return [];
}
let metrics = [];
if (browsertimeData) {
metrics = getMetricsFromBrowsertime(browsertimeData);
}
const files = fs.readdirSync(
path.join(dir, 'data', 'video', 'images', run + '')
);
const timings = [];
for (let file of files) {
timings.push({ time: file.replace(/\D/g, ''), file });
}
const maxTiming = timings.slice(-1)[0].time;
const toTheFront = [];
// We step 100 ms each step ... but if you wanna show all and the last change is late
// use 200 ms
const step =
maxTiming > 10000 && (options.filmstrip && options.filmstrip.showAll)
? 200
: 100;
let fileName = '';
for (let i = 0; i <= Number(maxTiming) + step; i = i + step) {
const entry = findFrame(timings, i);
const timingMetrics = findTimings(metrics, i - step, i);
if (
entry.file !== fileName ||
timingMetrics.length > 0 ||
(options.filmstrip && options.filmstrip.showAll)
) {
toTheFront.push({
time: i / 1000,
file: entry.file,
timings: timingMetrics
});
}
fileName = entry.file;
}
return toTheFront;
}
};

View File

@ -15,6 +15,8 @@ const isEmpty = require('lodash.isempty');
const summaryBoxesSetup = require('./setup/summaryBoxes'),
detailedSetup = require('./setup/detailed');
const filmstrip = require('./filmstrip');
const TIME_FORMAT = 'YYYY-MM-DD HH:mm:ss';
class HTMLBuilder {
@ -221,6 +223,17 @@ class HTMLBuilder {
const pageSummaries = this.pageSummaries.filter(
summary => !!get(pageInfo.data, [summary.id, 'pageSummary'])
);
// We use this for the filmstrip and in the future we should use the data there
// as median run all over in the HTML
const medianPageInfo = runPages[medianRun.runIndex - 1];
let filmstripData = medianPageInfo.data.browsertime
? filmstrip.getFilmstrip(
medianPageInfo.data.browsertime.run,
medianRun.runIndex,
this.storageManager.getFullPathToURLDir(url),
options
)
: [];
let data = {
daurl: url,
daurlAlias,
@ -234,6 +247,7 @@ class HTMLBuilder {
hasScreenShots: dataCollector.browsertimeScreenshots,
screenShotType: dataCollector.browsertimeScreenshotsType,
css,
filmstrip: filmstripData,
h: helpers,
JSON: JSON,
markdown: markdown,
@ -269,6 +283,15 @@ class HTMLBuilder {
this.timestamp
);
const filmstripData = pageInfo.data.browsertime
? filmstrip.getFilmstrip(
pageInfo.data.browsertime.run,
iteration,
this.storageManager.getFullPathToURLDir(url),
options
)
: [];
const pageRuns = this.pageRuns.filter(
run => !!get(pageInfo.data, [run.id, 'run'])
);
@ -290,6 +313,7 @@ class HTMLBuilder {
css,
h: helpers,
urlLink: './index.html',
filmstrip: filmstripData,
JSON: JSON,
markdown: markdown,
rootPath: this.storageManager.rootPathFromUrl(url),

View File

@ -24,6 +24,7 @@ module.exports = {
this.dataCollector = new DataCollector(context.resultUrls);
this.maxAssets = get(options, 'maxAssets', 20);
this.alias = {};
this.context = context;
// we have a couple of default regitered datatypes
this.collectDataFrom = [
'browsertime.run',

View File

@ -0,0 +1,32 @@
.filmstrip {
padding-bottom: 20px;
}
.videoframe {
vertical-align: top;
display: inline-block;
padding: 4px;
background-color: #fff;
border: 1px solid #ddd;
border-radius: 4px;
width:100%;
}
.videoframe.blue {
border: 2px solid $color--blue;
}
.videoframetime {
text-align: center;
display: block;
}
.videoframetext {
text-align: left;
display: block;
line-height: 1.2em;
font-size: 0.8em;
white-space: nowrap;
margin-bottom: 0.2em;
}

View File

@ -43,3 +43,4 @@
@import 'components/chartist';
@import 'components/chartist-plugin-tooltip';
@import 'components/chartistExtras';
@import 'components/filmstrip';

View File

@ -0,0 +1,59 @@
mixin frame(filmstrip, iteration)
- const frameClass = filmstrip[iteration].timings.length > 0 ? 'videoframe blue' : 'videoframe'
a(href=path + filmstrip[iteration].file)
img(src=path + filmstrip[iteration].file, alt='', class=frameClass)
span.videoframetime #{filmstrip[iteration].time} s
each timing in filmstrip[iteration].timings
span.videoframetext #{timing.name ? timing.name : timing.metric}
b #{h.time.ms(timing.value)}
a
h2 Filmstrip
- const videoIndex = medianRun ? medianRun.runIndex : iteration;
- const path = 'data/video/images/' + videoIndex +'/';
if (options.filmstrip && options.filmstrip.showAll)
p
| Showing all the filmstrips. Set
code --filmstrip.showAll
| to false to only show the ones that contains a change.
else
p
| Use
code --filmstrip.showAll
| to show all filmstrips.
if filmstrip
- for (let i=0; i<filmstrip.length; i=i+4)
.row
if options.mobile
.two.columns.filmstrip
+frame(filmstrip, i)
if filmstrip[i+1]
.two.columns.filmstrip
+frame(filmstrip, i+1)
if filmstrip[i+2]
.two.columns.filmstrip
+frame(filmstrip, i+2)
if filmstrip[i+3]
.two.columns.filmstrip
+frame(filmstrip, i+3)
if filmstrip[i+4]
.two.columns.filmstrip
+frame(filmstrip, i+4)
if filmstrip[i+5]
.two.columns.filmstrip
+frame(filmstrip, i+5)
else
.three.columns.filmstrip
+frame(filmstrip, i)
if filmstrip[i+1]
.three.columns.filmstrip
+frame(filmstrip, i+1)
if filmstrip[i+2]
.three.columns.filmstrip
+frame(filmstrip, i+2)
if filmstrip[i+3]
.three.columns.filmstrip
+frame(filmstrip, i+3)

View File

@ -121,6 +121,10 @@ block content
section#video-panel
include ../video/index.pug
if options.browsertime.video && options.videoParams.createFilmstrip
section#filmstrip-panel
include ../filmstrip/index.pug
if d.coach && d.coach.run
section#coach-panel
include ../coach/index.pug

View File

@ -9,7 +9,10 @@
| Metrics
if options.browsertime.video
a(id='video', href='#video', onclick='return selectTab(this, true);')
| Video
| Video
if options.browsertime.video && options.videoParams.createFilmstrip
a(id='filmstrip', href='#filmstrip', onclick='return selectTab(this, true);')
| Filmstrip
if d.coach && d.coach.run
a(id='coach', href='#coach', onclick='return selectTab(this, true);')
| Coach

View File

@ -120,6 +120,10 @@ block content
section#video-panel
include ../video/index.pug
if options.browsertime.video && options.videoParams.createFilmstrip
section#filmstrip-panel
include ../filmstrip/index.pug
if d.coach && d.coach.pageSummary
section#coach-panel
include ../coach/index.pug

View File

@ -9,7 +9,10 @@
| Metrics
if options.browsertime.video
a(id='video', href='#video', onclick='return selectTab(this, true);')
| Video
| Video
if options.browsertime.video && options.videoParams.createFilmstrip
a(id='filmstrip', href='#filmstrip', onclick='return selectTab(this, true);')
| Filmstrip
if d.coach && d.coach.pageSummary
a(id='coach', href='#coach', onclick='return selectTab(this, true);')
| Coach