diff --git a/lib/chrome/longTaskMetrics.js b/lib/chrome/longTaskMetrics.js index 74ec0970c..47735bf4a 100644 --- a/lib/chrome/longTaskMetrics.js +++ b/lib/chrome/longTaskMetrics.js @@ -37,7 +37,29 @@ export function longTaskMetrics(result, options) { let longTasksAfterLoadEventEnd = 0; let lastLongTask = 0; - for (let longTask of result.browserScripts.pageinfo.longTask) { + let longTasks = result.browserScripts.pageinfo.longTask; + // A soft navigation keeps the document, so the buffer also has the long + // tasks of earlier routes. The paint metrics are relative to the soft + // navigation start, so make the long tasks relative to it too. + const softNavigation = getProperty( + result, + 'browserScripts.timings.softNavigations', + [] + ).at(-1); + if (softNavigation) { + longTasks = longTasks + .filter(longTask => + longTask.navigationId === undefined + ? longTask.startTime >= softNavigation.startTime + : longTask.navigationId === softNavigation.navigationId + ) + .map(longTask => ({ + ...longTask, + startTime: longTask.startTime - softNavigation.startTime + })); + } + + for (let longTask of longTasks) { totalDuration += longTask.duration; if (longTask.startTime > lastLongTask) { lastLongTask = longTask.startTime; @@ -73,7 +95,7 @@ export function longTaskMetrics(result, options) { const cpu = { longTasks: { - tasks: result.browserScripts.pageinfo.longTask.length, + tasks: longTasks.length, totalDuration, totalBlockingTime: Number(totalBlockingTime.toFixed(0)), maxPotentialFid: Number(maxPotentialFid.toFixed(0)), diff --git a/lib/chrome/webdriver/chromium.js b/lib/chrome/webdriver/chromium.js index d9b945831..b8fbcfab1 100644 --- a/lib/chrome/webdriver/chromium.js +++ b/lib/chrome/webdriver/chromium.js @@ -245,6 +245,7 @@ export class Chromium { e.duration = event.duration; e.name = event.name; e.startTime = event.startTime; + e.navigationId = event.navigationId; e.attribution = []; for (let at of event.attribution) { const a = {}; diff --git a/test/unittests/longTaskMetricsTest.js b/test/unittests/longTaskMetricsTest.js new file mode 100644 index 000000000..b53b3ed3d --- /dev/null +++ b/test/unittests/longTaskMetricsTest.js @@ -0,0 +1,48 @@ +import test from 'ava'; + +import { longTaskMetrics } from '../../lib/chrome/longTaskMetrics.js'; + +function result(longTask, softNavigations) { + return { + browserScripts: { + timings: { + paintTiming: { 'first-contentful-paint': 40 }, + softNavigations + }, + pageinfo: { longTask } + } + }; +} + +const tasks = [ + { startTime: 1000, duration: 250, navigationId: 'hard' }, + { startTime: 5000, duration: 250, navigationId: 'route-one' }, + { startTime: 9000, duration: 250, navigationId: 'route-two' } +]; + +test('Total Blocking Time uses every long task after FCP for a page load', t => { + const cpu = longTaskMetrics(result([{ startTime: 1000, duration: 250 }]), {}); + t.is(cpu.longTasks.totalBlockingTime, 200); +}); + +test('Total Blocking Time only uses the long tasks of the soft navigation', t => { + const cpu = longTaskMetrics( + result(tasks, [{ navigationId: 'route-two', startTime: 8950 }]), + {} + ); + t.is(cpu.longTasks.tasks, 1); + t.is(cpu.longTasks.totalBlockingTime, 200); + t.is(cpu.longTasks.lastLongTask, 50); +}); + +test('Soft navigation long tasks without navigationId use the start time', t => { + const cpu = longTaskMetrics( + result( + tasks.map(({ startTime, duration }) => ({ startTime, duration })), + [{ navigationId: 'route-two', startTime: 8950 }] + ), + {} + ); + t.is(cpu.longTasks.tasks, 1); + t.is(cpu.longTasks.totalBlockingTime, 200); +});