Skip to content

Commit

Permalink
Merge remote-tracking branch 'origin/main' into rtt-estimate-origin-c…
Browse files Browse the repository at this point in the history
…oarse
  • Loading branch information
connorjclark committed Jun 12, 2023
2 parents 271a105 + 18a8c28 commit ba90631
Show file tree
Hide file tree
Showing 52 changed files with 865 additions and 427 deletions.
10 changes: 4 additions & 6 deletions cli/test/smokehouse/__snapshots__/report-assert-test.js.snap
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
exports[`getAssertionReport works (multiple failing) 1`] = `
"X difference at cumulative-layout-shift audit.details.items.length
expected: []
found: [{\\"cumulativeLayoutShiftMainFrame\\":0.13570762803819444,\\"totalCumulativeLayoutShift\\":0.13570762803819444}]
found: [{\\"cumulativeLayoutShiftMainFrame\\":0.13570762803819444}]
X difference at cumulative-layout-shift audit.details.blah
Expand All @@ -24,8 +24,7 @@ exports[`getAssertionReport works (multiple failing) 1`] = `
\\"type\\": \\"debugdata\\",
\\"items\\": [
{
\\"cumulativeLayoutShiftMainFrame\\": 0.13570762803819444,
\\"totalCumulativeLayoutShift\\": 0.13570762803819444
\\"cumulativeLayoutShiftMainFrame\\": 0.13570762803819444
}
]
}
Expand All @@ -35,7 +34,7 @@ exports[`getAssertionReport works (multiple failing) 1`] = `
exports[`getAssertionReport works (trivial failing) 1`] = `
"X difference at cumulative-layout-shift audit.details.items.length
expected: []
found: [{\\"cumulativeLayoutShiftMainFrame\\":0.13570762803819444,\\"totalCumulativeLayoutShift\\":0.13570762803819444}]
found: [{\\"cumulativeLayoutShiftMainFrame\\":0.13570762803819444}]
found result:
{
Expand All @@ -51,8 +50,7 @@ exports[`getAssertionReport works (trivial failing) 1`] = `
\\"type\\": \\"debugdata\\",
\\"items\\": [
{
\\"cumulativeLayoutShiftMainFrame\\": 0.13570762803819444,
\\"totalCumulativeLayoutShift\\": 0.13570762803819444
\\"cumulativeLayoutShiftMainFrame\\": 0.13570762803819444
}
]
}
Expand Down
16 changes: 15 additions & 1 deletion cli/test/smokehouse/test-definitions/dobetterweb.js
Original file line number Diff line number Diff line change
Expand Up @@ -574,6 +574,20 @@ const expectations = {
},
},
},
'network-rtt': {
details: {
items: [
{origin: 'http://localhost:10200', rtt: '>0'},
],
},
},
'network-server-latency': {
details: {
items: [
{origin: 'http://localhost:10200', serverResponseTime: '>0'},
],
},
},
'metrics': {
// Flaky in DevTools
_excludeRunner: 'devtools',
Expand All @@ -585,7 +599,7 @@ const expectations = {
},
'largest-contentful-paint-element': {
score: null,
displayValue: '1 element found',
displayValue: /\d+\xa0ms/,
details: {
items: [
{
Expand Down
1 change: 0 additions & 1 deletion cli/test/smokehouse/test-definitions/perf-frame-metrics.js
Original file line number Diff line number Diff line change
Expand Up @@ -78,7 +78,6 @@ const expectations = {
largestContentfulPaintAllFrames: '<5000',
cumulativeLayoutShift: '0.133 +/- 0.001',
cumulativeLayoutShiftMainFrame: '0.001 +/- 0.0005',
totalCumulativeLayoutShift: '0.001 +/- 0.0005',
},
{
lcpInvalidated: false,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -144,7 +144,7 @@ const expectations = {
audits: {
'largest-contentful-paint-element': {
score: null,
displayValue: '1 element found',
displayValue: /\d+\xa0ms/,
details: {
items: {
0: {
Expand Down
7 changes: 5 additions & 2 deletions core/audits/audit.js
Original file line number Diff line number Diff line change
Expand Up @@ -348,12 +348,14 @@ class Audit {
/**
* @param {typeof Audit} audit
* @param {string | LH.IcuMessage} errorMessage
* @param {string=} errorStack
* @return {LH.RawIcu<LH.Audit.Result>}
*/
static generateErrorAuditResult(audit, errorMessage) {
static generateErrorAuditResult(audit, errorMessage, errorStack) {
return Audit.generateAuditResult(audit, {
score: null,
errorMessage,
errorStack,
});
}

Expand All @@ -371,7 +373,7 @@ class Audit {
let scoreDisplayMode = audit.meta.scoreDisplayMode || Audit.SCORING_MODES.BINARY;

// But override if product contents require it.
if (product.errorMessage) {
if (product.errorMessage !== undefined) {
// Error result.
scoreDisplayMode = Audit.SCORING_MODES.ERROR;
} else if (product.notApplicable) {
Expand Down Expand Up @@ -407,6 +409,7 @@ class Audit {
displayValue: product.displayValue,
explanation: product.explanation,
errorMessage: product.errorMessage,
errorStack: product.errorStack,
warnings: product.warnings,

details: product.details,
Expand Down
80 changes: 49 additions & 31 deletions core/audits/largest-contentful-paint-element.js
Original file line number Diff line number Diff line change
Expand Up @@ -50,19 +50,44 @@ class LargestContentfulPaintElement extends Audit {
}

/**
* @param {LH.Artifacts} artifacts
* @param {LH.Artifacts.MetricComputationDataInput} metricComputationData
* @param {LH.Audit.Context} context
* @return {Promise<LH.Audit.Details.Table|undefined>}
* @return {Promise<number|undefined>}
*/
static async makePhaseTable(artifacts, context) {
const trace = artifacts.traces[Audit.DEFAULT_PASS];
const devtoolsLog = artifacts.devtoolsLogs[Audit.DEFAULT_PASS];
const gatherContext = artifacts.GatherContext;
const metricComputationData = {trace, devtoolsLog, gatherContext,
settings: context.settings, URL: artifacts.URL};
static async getOptionalLCPMetric(metricComputationData, context) {
try {
const {timing: metricLcp} =
await LargestContentfulPaint.request(metricComputationData, context);
return metricLcp;
} catch {}
}

/**
* @param {LH.Artifacts} artifacts
* @return {LH.Audit.Details.Table|undefined}
*/
static makeElementTable(artifacts) {
const lcpElement = artifacts.TraceElements
.find(element => element.traceEventType === 'largest-contentful-paint');
if (!lcpElement) return;

/** @type {LH.Audit.Details.Table['headings']} */
const headings = [
{key: 'node', valueType: 'node', label: str_(i18n.UIStrings.columnElement)},
];

const {timing: metricLcp} =
await LargestContentfulPaint.request(metricComputationData, context);
const lcpElementDetails = [{node: Audit.makeNodeItem(lcpElement.node)}];

return Audit.makeTableDetails(headings, lcpElementDetails);
}

/**
* @param {number} metricLcp
* @param {LH.Artifacts.MetricComputationDataInput} metricComputationData
* @param {LH.Audit.Context} context
* @return {Promise<LH.Audit.Details.Table>}
*/
static async makePhaseTable(metricLcp, metricComputationData, context) {
const {ttfb, loadStart, loadEnd} = await LCPBreakdown.request(metricComputationData, context);

let loadDelay = 0;
Expand Down Expand Up @@ -102,36 +127,29 @@ class LargestContentfulPaintElement extends Audit {
* @return {Promise<LH.Audit.Product>}
*/
static async audit(artifacts, context) {
const lcpElement = artifacts.TraceElements
.find(element => element.traceEventType === 'largest-contentful-paint');
const lcpElementDetails = [];
if (lcpElement) {
lcpElementDetails.push({
node: Audit.makeNodeItem(lcpElement.node),
});
}

/** @type {LH.Audit.Details.Table['headings']} */
const headings = [
{key: 'node', valueType: 'node', label: str_(i18n.UIStrings.columnElement)},
];
const trace = artifacts.traces[Audit.DEFAULT_PASS];
const devtoolsLog = artifacts.devtoolsLogs[Audit.DEFAULT_PASS];
const gatherContext = artifacts.GatherContext;
const metricComputationData = {trace, devtoolsLog, gatherContext,
settings: context.settings, URL: artifacts.URL};

const elementTable = Audit.makeTableDetails(headings, lcpElementDetails);
const elementTable = this.makeElementTable(artifacts);
if (!elementTable) return {score: null, notApplicable: true};

const items = [elementTable];
if (elementTable.items.length) {
const phaseTable = await this.makePhaseTable(artifacts, context);
if (phaseTable) items.push(phaseTable);
let displayValue;

const metricLcp = await this.getOptionalLCPMetric(metricComputationData, context);
if (metricLcp) {
displayValue = str_(i18n.UIStrings.ms, {timeInMs: metricLcp});
const phaseTable = await this.makePhaseTable(metricLcp, metricComputationData, context);
items.push(phaseTable);
}

const details = Audit.makeListDetails(items);

const displayValue = str_(i18n.UIStrings.displayValueElementsFound,
{nodeCount: lcpElementDetails.length});

return {
score: 1,
notApplicable: lcpElementDetails.length === 0,
displayValue,
details,
};
Expand Down
2 changes: 0 additions & 2 deletions core/audits/metrics.js
Original file line number Diff line number Diff line change
Expand Up @@ -11,10 +11,8 @@ import {TimingSummary} from '../computed/metrics/timing-summary.js';
const DECIMAL_METRIC_KEYS = new Set([
'cumulativeLayoutShift',
'cumulativeLayoutShiftMainFrame',
'totalCumulativeLayoutShift',
'observedCumulativeLayoutShift',
'observedCumulativeLayoutShiftMainFrame',
'observedTotalCumulativeLayoutShift',
]);

class Metrics extends Audit {
Expand Down
6 changes: 6 additions & 0 deletions core/audits/network-requests.js
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import {Audit} from './audit.js';
import UrlUtils from '../lib/url-utils.js';
import {NetworkRecords} from '../computed/network-records.js';
import {MainResource} from '../computed/main-resource.js';
import {EntityClassification} from '../computed/entity-classification.js';

class NetworkRequests extends Audit {
/**
Expand All @@ -31,6 +32,8 @@ class NetworkRequests extends Audit {
static async audit(artifacts, context) {
const devtoolsLog = artifacts.devtoolsLogs[Audit.DEFAULT_PASS];
const records = await NetworkRecords.request(devtoolsLog, context);
const classifiedEntities = await EntityClassification.request(
{URL: artifacts.URL, devtoolsLog}, context);
const earliestRendererStartTime = records.reduce(
(min, record) => Math.min(min, record.rendererStartTime),
Infinity
Expand Down Expand Up @@ -61,6 +64,8 @@ class NetworkRequests extends Audit {
((record.frameId === mainFrameId) || undefined) :
undefined;

const entity = classifiedEntities.entityByUrl.get(record.url);

return {
url: UrlUtils.elideDataURI(record.url),
sessionTargetType: record.sessionTargetType,
Expand All @@ -77,6 +82,7 @@ class NetworkRequests extends Audit {
priority: record.priority,
isLinkPreload,
experimentalFromMainFrame,
entity: entity?.name,
lrEndTimeDeltaMs: endTimeDeltaMs, // Only exists on Lightrider runs
lrTCPMs: TCPMs, // Only exists on Lightrider runs
lrRequestMs: requestMs, // Only exists on Lightrider runs
Expand Down
2 changes: 1 addition & 1 deletion core/audits/server-response-time.js
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,7 @@ class ServerResponseTime extends Audit {
*/
static calculateResponseTime(record) {
const timing = record.timing;
return timing ? timing.receiveHeadersEnd - timing.sendEnd : 0;
return timing ? timing.receiveHeadersStart - timing.sendEnd : 0;
}

/**
Expand Down
16 changes: 1 addition & 15 deletions core/computed/metrics/cumulative-layout-shift.js
Original file line number Diff line number Diff line change
Expand Up @@ -101,19 +101,10 @@ class CumulativeLayoutShift {
return maxScore;
}

/**
* Sum all layout shift events from the entire trace.
* @param {Array<LayoutShiftEvent>} layoutShiftEvents
* @return {number}
*/
static calculateTotalCumulativeLayoutShift(layoutShiftEvents) {
return layoutShiftEvents.reduce((sum, e) => sum += e.weightedScore, 0);
}

/**
* @param {LH.Trace} trace
* @param {LH.Artifacts.ComputedContext} context
* @return {Promise<{cumulativeLayoutShift: number, cumulativeLayoutShiftMainFrame: number, totalCumulativeLayoutShift: number}>}
* @return {Promise<{cumulativeLayoutShift: number, cumulativeLayoutShiftMainFrame: number}>}
*/
static async compute_(trace, context) {
const processedTrace = await ProcessedTrace.request(trace, context);
Expand All @@ -122,14 +113,9 @@ class CumulativeLayoutShift {
CumulativeLayoutShift.getLayoutShiftEvents(processedTrace);
const mainFrameShiftEvents = allFrameShiftEvents.filter(e => e.isMainFrame);

// The original Cumulative Layout Shift metric, the sum of all main-frame shift events.
const totalCumulativeLayoutShift =
CumulativeLayoutShift.calculateTotalCumulativeLayoutShift(mainFrameShiftEvents);

return {
cumulativeLayoutShift: CumulativeLayoutShift.calculate(allFrameShiftEvents),
cumulativeLayoutShiftMainFrame: CumulativeLayoutShift.calculate(mainFrameShiftEvents),
totalCumulativeLayoutShift,
};
}
}
Expand Down
14 changes: 8 additions & 6 deletions core/computed/metrics/time-to-first-byte.js
Original file line number Diff line number Diff line change
Expand Up @@ -41,15 +41,17 @@ class TimeToFirstByte extends NavigationMetric {
* @return {Promise<LH.Artifacts.Metric>}
*/
static async computeObservedMetric(data, context) {
const {processedNavigation} = data;
const timeOriginTs = processedNavigation.timestamps.timeOrigin;
const mainResource = await MainResource.request(data, context);
if (!mainResource.timing) {
throw new Error('missing timing for main resource');
}

// Technically TTFB is the start of the response headers not the end.
// That signal isn't available to us so we use header end time as a best guess.
const timestamp = mainResource.responseHeadersEndTime * 1000;
const {processedNavigation} = data;
const timeOriginTs = processedNavigation.timestamps.timeOrigin;
const timestampMs =
mainResource.timing.requestTime * 1000 + mainResource.timing.receiveHeadersStart;
const timestamp = timestampMs * 1000;
const timing = (timestamp - timeOriginTs) / 1000;

return {timing, timestamp};
}
}
Expand Down
3 changes: 0 additions & 3 deletions core/computed/metrics/timing-summary.js
Original file line number Diff line number Diff line change
Expand Up @@ -65,7 +65,6 @@ class TimingSummary {
const {
cumulativeLayoutShift,
cumulativeLayoutShiftMainFrame,
totalCumulativeLayoutShift,
} = cumulativeLayoutShiftValues || {};

/** @type {LH.Artifacts.TimingSummary} */
Expand All @@ -89,7 +88,6 @@ class TimingSummary {
maxPotentialFID: maxPotentialFID?.timing,
cumulativeLayoutShift,
cumulativeLayoutShiftMainFrame,
totalCumulativeLayoutShift,

lcpLoadStart: lcpBreakdown?.loadStart,
lcpLoadEnd: lcpBreakdown?.loadEnd,
Expand Down Expand Up @@ -123,7 +121,6 @@ class TimingSummary {
observedDomContentLoadedTs: processedNavigation?.timestamps.domContentLoaded,
observedCumulativeLayoutShift: cumulativeLayoutShift,
observedCumulativeLayoutShiftMainFrame: cumulativeLayoutShiftMainFrame,
observedTotalCumulativeLayoutShift: totalCumulativeLayoutShift,

// Include some visual metrics from speedline
observedFirstVisualChange: speedline.first,
Expand Down
18 changes: 13 additions & 5 deletions core/gather/driver/execution-context.js
Original file line number Diff line number Diff line change
Expand Up @@ -125,14 +125,22 @@ class ExecutionContext {

this._session.setNextProtocolTimeout(timeout);
const response = await this._session.sendCommand('Runtime.evaluate', evaluationParams);
if (response.exceptionDetails) {

const ex = response.exceptionDetails;
if (ex) {
// An error occurred before we could even create a Promise, should be *very* rare.
// Also occurs when the expression is not valid JavaScript.
const errorMessage = response.exceptionDetails.exception ?
response.exceptionDetails.exception.description :
response.exceptionDetails.text;
return Promise.reject(new Error(`Evaluation exception: ${errorMessage}`));
const elidedExpression = expression.replace(/\s+/g, ' ').substring(0, 100);
const messageLines = [
'Runtime.evaluate exception',
`Expression: ${elidedExpression}\n---- (elided)`,
!ex.stackTrace ? `Parse error at: ${ex.lineNumber + 1}:${ex.columnNumber + 1}` : null,
ex.exception?.description || ex.text,
].filter(Boolean);
const evaluationError = new Error(messageLines.join('\n'));
return Promise.reject(evaluationError);
}

// Protocol should always return a 'result' object, but it is sometimes undefined. See #6026.
if (response.result === undefined) {
return Promise.reject(
Expand Down
2 changes: 2 additions & 0 deletions core/gather/gatherers/inspector-issues.js
Original file line number Diff line number Diff line change
Expand Up @@ -75,6 +75,8 @@ class InspectorIssues extends FRGatherer {
quirksModeIssue: [],
cookieIssue: [],
sharedArrayBufferIssue: [],
stylesheetLoadingIssue: [],
federatedAuthUserInfoRequestIssue: [],
};
const keys = /** @type {Array<keyof LH.Artifacts['InspectorIssues']>} */(Object.keys(artifact));
for (const key of keys) {
Expand Down
Loading

0 comments on commit ba90631

Please sign in to comment.