Skip to content

Commit

Permalink
feat: add budgets section to md report
Browse files Browse the repository at this point in the history
  • Loading branch information
BioPhoton authored Feb 11, 2023
2 parents c0871c2 + a6ffc77 commit 55e11f1
Show file tree
Hide file tree
Showing 14 changed files with 10,558 additions and 300 deletions.
1,369 changes: 1,113 additions & 256 deletions packages/cli/docs/raw/uf-inc-budget.json

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion packages/cli/src/lib/commands/assert/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import { readBudgets } from './utils/budgets';
import { readFile } from '../../core/file';
import { getCollectCommandOptionsFromArgv } from '../collect/utils/params';
import { RcJson } from '../../types';
import { generateMdReport } from '../collect/utils/persist/utils';
import { generateMdReport } from './utils/md-report';

export const assertCommand: YargsCommandObject = {
command: 'assert',
Expand Down
19 changes: 14 additions & 5 deletions packages/cli/src/lib/commands/assert/utils/md-report.test.ts
Original file line number Diff line number Diff line change
@@ -1,11 +1,13 @@
import { userFlowReportToMdTable } from './md-report';
import { getBudgetTable, getStepsTable } from './md-report';
import FlowResult from 'lighthouse/types/lhr/flow';
import { getReportContent } from 'test-data';
import { writeFileSync } from 'fs';
import { ReducedReport } from '../../collect/utils/report/types';
import { createReducedReport, enrichReducedReportWithBaseline } from '../../collect/utils/report/utils';

const lhr8 = getReportContent<any>('lhr-8.json');
const lhr9 = getReportContent<FlowResult>('lhr-9.json');
const lhr9budgets = getReportContent<FlowResult>('lhr-9-budgets.json');
const LHRREDUCEDMD = getReportContent('lhr-9_reduced.md');
const lhr9Ex2 = getReportContent<FlowResult>('lhr-9-ex-2.json');
const lhr9reduced = getReportContent<ReducedReport>('lhr-9_reduced.json');
Expand Down Expand Up @@ -34,15 +36,22 @@ describe('md-table', () => {
expect(enrichedReducedLhr9).toEqual(lhr9ReducedBaseline);
});

it('should print MD table if userFlowReportToMdTable is called with a reduced result', () => {
it('should print MD table if getStepsTable is called with a reduced result', () => {
const reducedLhr9 = createReducedReport(lhr9);
const mdTable = userFlowReportToMdTable(reducedLhr9);
const mdTable = getStepsTable(reducedLhr9);
expect(mdTable).toEqual(LHRREDUCEDMD);
});

it('should return a Md table comparing to reports if userFlowReportToMdTable is passed a baseline report', () => {
it('should return a Md table comparing to reports if getStepsTable is passed a baseline report', () => {
const reducedLhr9 = createReducedReport(lhr9);
const mdTable = userFlowReportToMdTable(reducedLhr9, lhr9Ex2);
const mdTable = getStepsTable(reducedLhr9, lhr9Ex2);
expect(mdTable).toEqual(LHRREDUCEDCompareMD);
});

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('| Metric | Measurement | Over Budget |');
});
});
104 changes: 95 additions & 9 deletions packages/cli/src/lib/commands/assert/utils/md-report.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,80 @@
import { FractionResults, ReducedFlowStep, ReducedReport } from '../../collect/utils/report/types';
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';

const budgetsSymbol = '🔒'

export function generateMdReport(flowResult: ReducedReport): string {
const name = flowResult.name;
const dateTime = `Date/Time: ${style(new Date().toISOString().replace('T', ' ').split('.')[0].slice(0, -3))} `;
const stepsTable = getStepsTable(flowResult);

let md = `${headline(name)}${NEW_LINE}
${dateTime}${NEW_LINE}
${stepsTable}${NEW_LINE}
`;

const budgetsTable = getBudgetTable(flowResult);
if(budgetsTable !== '') {
md += details(`${budgetsSymbol} Budgets`, budgetsTable) + NEW_LINE;
}


return md;
}

/**
* ## Navigation report (127.0.0.1/)
*
* | Resource Type | Requests | Transfer Size | Over Budget |
* | :-----------: | :------: | :-----------: | :---------: |
* | Total | 205774 | total | 204750 |
* | Script | 101958 | script | - |
* | Third-party | 97308 | third-party | - |
*
* | Metric | Measurement | Over Budget |
* | :--------------------: | :---------: | :---------: |
* | First Meaningful Paint | 2043 | 43 |
* | Time to Interactive | 4745 | - |
*
*/
export function getBudgetTable(reducedReport: ReducedReport, options: {heading: Hierarchy} = {heading: 3}): string {
const performanceBudgets = reducedReport.steps
.filter(({ resultsPerformanceBudget, resultsTimingBudget }) => resultsPerformanceBudget || resultsTimingBudget)
.map(({ name, resultsPerformanceBudget, resultsTimingBudget }) => ({
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, measurement + ' ms', overBudget ? `${overBudget} ms` : '-'] as (string|number)[]) || []
] : []
})
);
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.resultsTimingBudget !== undefined) {
md += style('Timing Budget') + NEW_LINE+ NEW_LINE;
md += (b.resultsPerformanceBudget ? NEW_LINE : '') + table(b.resultsTimingBudget) + NEW_LINE;
}
return md;
}).join(NEW_LINE) : '';
}

// import FlowResult from 'lighthouse/types/lhr/flow';

/**
* | Step Name | Gather Mode |Performance | Accessibility | BestPractices | Seo | PWA |
Expand All @@ -11,7 +83,7 @@ import { Alignment, table } from '../../../core/md/table';
* | Snap 1 | 3/3 | 22/5 | 5/2 | 7/10 | - |
* | TimeSpan 1 | 10/11 | - | 4/7 | 7/10 | - |
*/
export function userFlowReportToMdTable(reducedReport: ReducedReport, baselineResults?: any): string {
export function getStepsTable(reducedReport: ReducedReport, baselineResults?: any): string {
const reportCategories = Object.keys(reducedReport.steps[0].results);
const tableStepsArr = formatStepsForMdTable(reportCategories, reducedReport, baselineResults);
const alignOptions = headerAlignment(reportCategories);
Expand All @@ -29,13 +101,26 @@ function formatStepsForMdTable(reportCategories: string[], reducedReport: Reduce
}
return reducedReport.steps.map((step) => {
const results = reportCategories.map(category => extractResultsValue(step.results[category]));
// add 🔒 to gatherMode to indicate budgets
step.resultsPerformanceBudget && (step.gatherMode = step.gatherMode + ' 🔒' as any);
// add `budgetsSymbol` to gatherMode to indicate budgets
step.resultsPerformanceBudget && (step.gatherMode = step.gatherMode + ` ${budgetsSymbol}` as GatherMode);
return [step.name, step.gatherMode].concat(results);
});
}

function extractTableArr(reportCategories: string[], steps: any[]): string[][] {
function formatBytes(bytes: number, decimals = 2) {
if (!+bytes) return '0 Bytes'

const k = 1024
const dm = decimals < 0 ? 0 : decimals
const sizes = ['Bytes', 'KB', 'MB', 'GB', 'TB', 'PB', 'EB', 'ZB', 'YB']

const i = Math.floor(Math.log(bytes) / Math.log(k))

return `${parseFloat((bytes / Math.pow(k, i)).toFixed(dm))} ${sizes[i]}`
}


function extractTableArr(reportCategories: string[], steps: any[]): string[][] {
const tableHead = extractTableHead(reportCategories);
return [tableHead].concat(steps);
}
Expand All @@ -61,11 +146,11 @@ function extractResultsValue(stepResult?: number | FractionResults): string {
function extractEnrichedResults(step: ReducedFlowStep, category: string): string {
const result = extractResultsValue(step.results[category]);
const baseline = extractResultsValue(step.baseline![category]);
return resultWithBaselineComparison(result, baseline)
return resultWithBaselineComparison(result, baseline);
}

function resultWithBaselineComparison(result: string, baseline: string): string {
if (result === baseline) {
if (result === baseline) {
return result;
}
const resultNum = Number(result.replace('Ø ', '').split('/')[0]);
Expand All @@ -74,7 +159,8 @@ function resultWithBaselineComparison(result: string, baseline: string): string
return `${result} (${difference > 0 ? '+' : ''}${difference})`;
}

function headerAlignment(reportCategories: string[]): Alignment[] {
function headerAlignment(reportCategories: string[]): Alignment[] {
const reportFormats = reportCategories.map(_ => 'c');
return ['l', 'c'].concat(reportFormats) as Alignment[];
}

Original file line number Diff line number Diff line change
Expand Up @@ -5,11 +5,11 @@ import { join } from 'path';
import { writeFile } from '../../../../core/file';
import { existsSync, mkdirSync } from 'fs';
import { PersistFlowOptions } from './types';
import { generateMdReport } from './utils';
import { createReducedReport } from '../../../..';
import { generateStdoutReport } from '../persist/utils';
import { toReportName } from '../report/utils';
import { ReducedReport } from '../report/types';
import { generateMdReport } from '../../../assert/utils/md-report';

export async function persistFlow(
flow: UserFlow,
Expand Down
17 changes: 2 additions & 15 deletions packages/cli/src/lib/commands/collect/utils/persist/utils.ts
Original file line number Diff line number Diff line change
@@ -1,25 +1,12 @@
import { NEW_LINE } from '../../../../core/md/constants';
import { headline } from '../../../../core/md/headline';
import { style } from '../../../../core/md/font-style';
import { ReducedReport } from '../report/types';
import { userFlowReportToMdTable } from '../../../assert/utils/md-report';
import { getStepsTable } from '../../../assert/utils/md-report';

export function generateStdoutReport(flowResult: ReducedReport): string {
const dateTime = new Date().toISOString().replace('T', ' ').split('.')[0].slice(0, -3);
const mdTable = userFlowReportToMdTable(flowResult);
const mdTable = getStepsTable(flowResult);
return `# ${flowResult.name}\n\nDate/Time: ${dateTime}\n\n${mdTable}`;
}

export function generateMdReport(flowResult: ReducedReport): string {
const name = flowResult.name;
const dateTime = `Date/Time: ${style(new Date().toISOString().replace('T', ' ').split('.')[0].slice(0, -3))} `;
const mdTable = userFlowReportToMdTable(flowResult);

return `${headline(name)}${NEW_LINE}
${dateTime}${NEW_LINE}
${mdTable}`;
}

export function dateToIsoLikeString(date: Date): string {
return isoDateStringToIsoLikeString(date.toISOString());
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,10 @@ export function parseSteps(steps: FlowResult.Step[]): ReducedFlowStep[] {
results
};
if(step.lhr.audits['performance-budget']) {
reducedStep.resultsPerformanceBudget = step.lhr.audits['performance-budget'].details
reducedStep.resultsPerformanceBudget = step.lhr.audits['performance-budget'].details as any
}
if(step.lhr.audits['timing-budget']) {
reducedStep.resultsTimingBudget = step.lhr.audits['timing-budget'].details as any
}
return reducedStep;
});
Expand Down
10 changes: 7 additions & 3 deletions packages/cli/src/lib/commands/collect/utils/report/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,12 +3,15 @@ import Config from 'lighthouse/types/config';
import { CLI_MODES } from '../../../../global/cli-mode';
import { PickOne } from '../../../../core/types';
import FlowResult from 'lighthouse/types/lhr/flow';
import Details from 'lighthouse/types/lhr/audit-details';

type OverBudget = { overBudget: number };
type BudgetAssertion = (Budget.ResourceBudget & OverBudget | Budget.TimingBudget & OverBudget)[];
type BudgetAssertion = (Budget.ResourceBudget & OverBudget | Budget.TimingBudget & OverBudget);

type UfrSlice = PickOne<FlowResult>;
type LhrSlice = PickOne<FlowResult.Step['lhr']>;

export type GatherMode = FlowResult.Step['lhr']['gatherMode'];
/**
* Plucks key value from oroginal LH report
* @example
Expand All @@ -17,7 +20,7 @@ type LhrSlice = PickOne<FlowResult.Step['lhr']>;
* const f1: GatherModeSlice = {gatherMode: 'timespan'};
* const f2: GatherModeSlice = {gatherMode: 'snapshot'};
*/
type LhrGatherModeSlice = LhrSlice & { gatherMode: FlowResult.Step['lhr']['gatherMode'] };
type LhrGatherModeSlice = LhrSlice & { gatherMode: GatherMode };
type UfrNameSlice = UfrSlice & { name: string };


Expand All @@ -39,7 +42,8 @@ export type ReducedFlowStep =
name: string;
fetchTime: string;
results: ReducedFlowStepResult;
resultsPerformanceBudget?: any,
resultsPerformanceBudget?: Details.Table,
resultsTimingBudget?: Details.Table,
baseline?: ReducedFlowStepResult;
};

Expand Down
2 changes: 1 addition & 1 deletion packages/cli/src/lib/core/md/details.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ import { NEW_LINE } from './constants';
* {content}
* <details>
*/
export function details(title: string, content: string, cfg: { open: boolean } = { open: true }): string {
export function details(title: string, content: string, cfg: { open: boolean } = { open: false }): string {
return `<details ${cfg.open && 'open'}>${NEW_LINE}
<summary>${title}</summary>${NEW_LINE}
${content}${NEW_LINE}
Expand Down
3 changes: 2 additions & 1 deletion packages/cli/src/lib/core/md/table.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,8 @@ const alignString = new Map<Alignment, string>([['l', ':--'],['c', ':--:'],['r',
* | String 1 | 2 |
* | String 1 | 3 |
*/
export function table(data: string[][], align: Alignment[]): string {
export function table(data: (string|number)[][], align?: Alignment[]): string {
align = align || data[0].map(_ => 'c');
const _data = data.map((arr) => arr.join('|'));
const secondRow = align.map((s) => alignString.get(s)).join('|');
return formatCode(_data.shift() + '\n' + secondRow + '\n' + _data.join('\n'), 'markdown');
Expand Down
2 changes: 1 addition & 1 deletion packages/cli/src/lib/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ export { ERROR_PERSIST_FORMAT_WRONG } from './commands/collect/options/format.co
export { getGlobalOptionsFromArgv } from './global/utils';
export { SETUP_CONFIRM_MESSAGE } from './commands/init/constants';
export { PROMPT_COLLECT_URL } from './commands/collect/options/url.constant';
export { userFlowReportToMdTable } from './commands/assert/utils/md-report';
export { getStepsTable } from './commands/assert/utils/md-report';
export { PROMPT_COLLECT_UF_PATH } from './commands/collect/options/ufPath.constant';
export { PROMPT_PERSIST_OUT_PATH } from './commands/collect/options/outPath.constant';
export { PROMPT_PERSIST_FORMAT } from './commands/collect/options/format.constant';
Expand Down
12 changes: 12 additions & 0 deletions packages/test-data/src/lib/raw-reports/budget-table.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
## Navigation report (127.0.0.1/)

| Resource Type | Requests | Transfer Size | Over Budget |
| :-----------: | :------: | :-----------: | :---------: |
| Total | 205774 | total | 204750 |
| Script | 101958 | script | - |
| Third-party | 97308 | third-party | - |

| Metric | Measurement | Over Budget |
| :--------------------: | :---------: | :---------: |
| First Meaningful Paint | 2043 | 43 |
| Time to Interactive | 4745 | - |
Loading

0 comments on commit 55e11f1

Please sign in to comment.