diff --git a/packages/cli/src/lib/commands/assert/utils/md-report.test.ts b/packages/cli/src/lib/commands/assert/utils/md-report.test.ts index db5ad3218..36f66443f 100644 --- a/packages/cli/src/lib/commands/assert/utils/md-report.test.ts +++ b/packages/cli/src/lib/commands/assert/utils/md-report.test.ts @@ -51,7 +51,8 @@ describe('md-table', () => { it('should return a Md table if getBudgetTable is passed a baseline report', () => { const reducedLhr9 = createReducedReport(lhr9budgets); const mdTable = getBudgetTable(reducedLhr9); - expect(mdTable).toContain('| Resource Type | Requests | Transfer Size |'); + expect(mdTable).toContain('| Resource Type | Transfer Size | Over Budget |'); + expect(mdTable).toContain('| Resource Type | Requests | Over Budget |'); expect(mdTable).toContain('| Metric | Measurement | Over Budget |'); }); }); diff --git a/packages/cli/src/lib/commands/assert/utils/md-report.ts b/packages/cli/src/lib/commands/assert/utils/md-report.ts index 90ac72835..67ae72142 100644 --- a/packages/cli/src/lib/commands/assert/utils/md-report.ts +++ b/packages/cli/src/lib/commands/assert/utils/md-report.ts @@ -1,10 +1,14 @@ -import { FractionResults, GatherMode, ReducedFlowStep, ReducedReport } from '../../collect/utils/report/types'; -import { enrichReducedReportWithBaseline } from '../../collect/utils/report/utils'; -import { Alignment, table } from '../../../core/md/table'; -import { style } from '../../../core/md/font-style'; -import { headline, Hierarchy } from '../../../core/md/headline'; -import { NEW_LINE } from '../../../core/md/constants'; -import { details } from '../../../core/md/details'; +import {FractionResults, GatherMode, ReducedFlowStep, ReducedReport} from '../../collect/utils/report/types'; +import {enrichReducedReportWithBaseline} from '../../collect/utils/report/utils'; +import {Alignment, table} from '../../../core/md/table'; +import {style} from '../../../core/md/font-style'; +import {headline, Hierarchy} from '../../../core/md/headline'; +import {NEW_LINE} from '../../../core/md/constants'; +import {details} from '../../../core/md/details'; +import Budget from "lighthouse/types/lhr/budget"; +import TimingBudget = Budget.TimingBudget; +import Details from "lighthouse/types/lhr/audit-details"; +import Table = Details.Table; const budgetsSymbol = '🔒' @@ -19,7 +23,7 @@ ${stepsTable}${NEW_LINE} `; const budgetsTable = getBudgetTable(flowResult); - if(budgetsTable !== '') { + if (budgetsTable !== '') { md += details(`${budgetsSymbol} Budgets`, budgetsTable) + NEW_LINE; } @@ -42,48 +46,103 @@ ${stepsTable}${NEW_LINE} * | Time to Interactive | 4745 | - | * */ -export function getBudgetTable(reducedReport: ReducedReport, options: {heading: Hierarchy} = {heading: 3}): string { + +export function getBudgetTable(reducedReport: ReducedReport, options: { heading: Hierarchy } = {heading: 3}): string { const performanceBudgets = reducedReport.steps - .filter(({ resultsPerformanceBudget, resultsTimingBudget }) => resultsPerformanceBudget || resultsTimingBudget) - .map(({ name, resultsPerformanceBudget, resultsTimingBudget }) => ({ + .filter(({resourceCountsBudget, resourceSizesBudget, timingsBudget}) => resourceCountsBudget || resourceSizesBudget || timingsBudget) + .map(({name, resourceCountsBudget, resourceSizesBudget, timingsBudget}) => ({ name, - resultsPerformanceBudget: resultsPerformanceBudget !== undefined ? [ - resultsPerformanceBudget.headings - .map((h) => h.text as string), - ...resultsPerformanceBudget.items.map( - ({label, transferSize, requestCount, sizeOverBudget, countOverBudget}) => - [ - label, - requestCount, - formatBytes(transferSize as number), - sizeOverBudget ? formatBytes(sizeOverBudget as number) : '-', - countOverBudget || '-' - ] as (string|number)[]) || [] - ] : [], - resultsTimingBudget: resultsTimingBudget !== undefined ? [ - resultsTimingBudget.headings.map(h => h.text as string), - ...resultsTimingBudget.items.map(({label, measurement, overBudget}) => - [label, - typeof measurement === 'object' ? (measurement as any).value : measurement + ' ms' - , overBudget !== undefined ? `${overBudget} ms` : '-']) || [] - ] : [] + resultsSizeBudget: getResourceSizes(resourceSizesBudget), + resultsCountBudget: getResourceCounts(resourceCountsBudget), + resultsTimingBudget: getTimings(timingsBudget) }) ); return performanceBudgets.length ? performanceBudgets.map(b => { let md = headline(b.name, options.heading) + NEW_LINE + NEW_LINE; - if(b.resultsPerformanceBudget !== undefined) { - md += style('Resource Budget') + NEW_LINE+ NEW_LINE; - md += table(b.resultsPerformanceBudget) + NEW_LINE + NEW_LINE + if (b.resultsSizeBudget !== undefined) { + md += style('Resource Size Budget') + NEW_LINE + NEW_LINE; + md += table(b.resultsSizeBudget) + NEW_LINE + NEW_LINE } - if(b.resultsTimingBudget !== undefined) { - md += style('Timing Budget') + NEW_LINE+ NEW_LINE; - md += (b.resultsPerformanceBudget ? NEW_LINE : '') + table(b.resultsTimingBudget) + NEW_LINE; + if (b.resultsCountBudget !== undefined) { + md += style('Resource Count Budget') + NEW_LINE + NEW_LINE; + md += table(b.resultsCountBudget) + NEW_LINE + NEW_LINE + } + if (b.resultsTimingBudget !== undefined) { + md += style('Timing Budget') + NEW_LINE + NEW_LINE; + md += table(b.resultsTimingBudget) + NEW_LINE + NEW_LINE } return md; }).join(NEW_LINE) : ''; } +function getTimings(resultsTimingBudget: Table | undefined): undefined | string[][] { + if (resultsTimingBudget === undefined) { + return undefined + } else { + return [ + // [ {text: string, ...props } ] + resultsTimingBudget.headings.map((h: any) => h.text as string), + ...resultsTimingBudget.items.map(({label, measurement, overBudget}: any) => { + const row = []; + if(label === 'Cumulative Layout Shift') { + return [label + '', + measurement === undefined ? '-' : parseFloat(measurement.value).toFixed(2), + overBudget === undefined ? '-' : parseFloat(overBudget).toFixed(2), + ] + } else { + return [label + '', + measurement === undefined ? '-' : measurement + ' ms', + overBudget === undefined ? '-' : overBudget + ' ms' + ] + } + }) + ] + } +}; + +function getResourceSizes(resourceSizesBudget: Table | undefined): undefined | string[][] { + if (resourceSizesBudget === undefined) { + return undefined + } else { + return [ + resourceSizesBudget.headings + .filter((i: any) => ['label', 'transferSize', 'sizeOverBudget'].includes(i.key)) + .map((h: any) => h.text as string), + ...resourceSizesBudget.items + .map( + ({label, transferSize, sizeOverBudget}: any) => + [ + label+'', + formatBytes(transferSize), + sizeOverBudget ? formatBytes(sizeOverBudget) : '-' + ]) + ] + } +} + +function getResourceCounts(resourceCountsBudget: Table | undefined): undefined | string[][] { + if (resourceCountsBudget === undefined) { + return undefined + } else { + return [ + resourceCountsBudget.headings + .filter((i: any) => ['label', 'requestCount', 'countOverBudget'].includes(i.key)) + .map((h: any) => h.key === 'countOverBudget' ? 'Over Budget' : h.text as string), + ...resourceCountsBudget.items + .map( + ({label, requestCount, countOverBudget}: any) => + [ + label+'', + requestCount+'', + countOverBudget ? countOverBudget : '-' + ]) + ] + } +} + + + /** * | Step Name | Gather Mode |Performance | Accessibility | BestPractices | Seo | PWA | * | --------------- | ----------- | ------------- | ------------- | ---- | --- | @@ -110,7 +169,7 @@ function formatStepsForMdTable(reportCategories: string[], reducedReport: Reduce return reducedReport.steps.map((step) => { const results = reportCategories.map(category => extractResultsValue(step.results[category])); // add `budgetsSymbol` to gatherMode to indicate budgets - step.resultsPerformanceBudget && (step.gatherMode = step.gatherMode + ` ${budgetsSymbol}` as GatherMode); + step.resourceCountsBudget && (step.gatherMode = step.gatherMode + ` ${budgetsSymbol}` as GatherMode); return [step.name, step.gatherMode].concat(results); }); } @@ -139,7 +198,7 @@ function extractTableHead(reportCategories: string[]): string[] { } function extractFractionalResultValue(fractionResults: FractionResults): string { - const { totalWeight, numPassed, numPassableAudits } = fractionResults; + const {totalWeight, numPassed, numPassableAudits} = fractionResults; const value = numPassed + '/' + numPassableAudits; return totalWeight === 0 ? 'Ø ' + value : value; } diff --git a/packages/cli/src/lib/commands/collect/utils/report/lh-utils.ts b/packages/cli/src/lib/commands/collect/utils/report/lh-utils.ts index 0ca23f079..6bfb97b2e 100644 --- a/packages/cli/src/lib/commands/collect/utils/report/lh-utils.ts +++ b/packages/cli/src/lib/commands/collect/utils/report/lh-utils.ts @@ -24,10 +24,11 @@ export function parseSteps(steps: FlowResult.Step[]): ReducedFlowStep[] { results }; if(step.lhr.audits['performance-budget']) { - reducedStep.resultsPerformanceBudget = step.lhr.audits['performance-budget'].details as any + reducedStep.resourceCountsBudget = step.lhr.audits['performance-budget'].details as any + reducedStep.resourceSizesBudget = step.lhr.audits['performance-budget'].details as any } if(step.lhr.audits['timing-budget']) { - reducedStep.resultsTimingBudget = step.lhr.audits['timing-budget'].details as any + reducedStep.timingsBudget = step.lhr.audits['timing-budget'].details as any } return reducedStep; }); diff --git a/packages/cli/src/lib/commands/collect/utils/report/types.ts b/packages/cli/src/lib/commands/collect/utils/report/types.ts index 8b495f8d6..61bb89b82 100644 --- a/packages/cli/src/lib/commands/collect/utils/report/types.ts +++ b/packages/cli/src/lib/commands/collect/utils/report/types.ts @@ -42,8 +42,9 @@ export type ReducedFlowStep = name: string; fetchTime: string; results: ReducedFlowStepResult; - resultsPerformanceBudget?: Details.Table, - resultsTimingBudget?: Details.Table, + resourceCountsBudget?: Details.Table, + resourceSizesBudget?: Details.Table, + timingsBudget?: Details.Table, baseline?: ReducedFlowStepResult; };