Skip to content

Commit

Permalink
core: expose back compat as upgradeLhrForCompatability
Browse files Browse the repository at this point in the history
  • Loading branch information
connorjclark committed Jan 27, 2023
1 parent a2977ff commit 01a74a7
Show file tree
Hide file tree
Showing 5 changed files with 294 additions and 269 deletions.
3 changes: 3 additions & 0 deletions core/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ import {ReportGenerator} from '../report/generator/report-generator.js';
import {startTimespanGather} from './gather/timespan-runner.js';
import {snapshotGather} from './gather/snapshot-runner.js';
import {navigationGather} from './gather/navigation-runner.js';
import {Util} from '../shared/util.js';
import * as LH from '../types/lh.js';

/** @typedef {import('./legacy/gather/connections/connection.js').Connection} Connection */
Expand Down Expand Up @@ -151,6 +152,7 @@ function getAuditList() {
}

const traceCategories = Driver.traceCategories;
const upgradeLhrForCompatibility = Util.upgradeLhrForCompatibility;

export default lighthouse;
export {Audit} from './audits/audit.js';
Expand All @@ -168,5 +170,6 @@ export {
generateReport,
auditFlowArtifacts,
getAuditList,
upgradeLhrForCompatibility,
traceCategories,
};
126 changes: 4 additions & 122 deletions report/renderer/report-utils.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,6 @@
import {Util} from '../../shared/util.js';
import {Globals} from './report-globals.js';

const SCREENSHOT_PREFIX = 'data:image/jpeg;base64,';
const RATINGS = Util.RATINGS;

class ReportUtils {
Expand All @@ -17,108 +16,20 @@ class ReportUtils {
* compatible with current renderer.
* The LHR passed in is not mutated.
* TODO(team): we all agree the LHR shape change is technical debt we should fix
* @param {LH.Result} result
* @param {LH.Result} lhr
* @return {LH.ReportResult}
*/
static prepareReportResult(result) {
static prepareReportResult(lhr) {
// If any mutations happen to the report within the renderers, we want the original object untouched
const clone = /** @type {LH.ReportResult} */ (JSON.parse(JSON.stringify(result)));

// If LHR is older (≤3.0.3), it has no locale setting. Set default.
if (!clone.configSettings.locale) {
clone.configSettings.locale = 'en';
}
if (!clone.configSettings.formFactor) {
// @ts-expect-error fallback handling for emulatedFormFactor
clone.configSettings.formFactor = clone.configSettings.emulatedFormFactor;
}

clone.finalDisplayedUrl = Util.getFinalDisplayedUrl(clone);
clone.mainDocumentUrl = Util.getMainDocumentUrl(clone);

for (const audit of Object.values(clone.audits)) {
// Turn 'not-applicable' (LHR <4.0) and 'not_applicable' (older proto versions)
// into 'notApplicable' (LHR ≥4.0).
// @ts-expect-error tsc rightly flags that these values shouldn't occur.
// eslint-disable-next-line max-len
if (audit.scoreDisplayMode === 'not_applicable' || audit.scoreDisplayMode === 'not-applicable') {
audit.scoreDisplayMode = 'notApplicable';
}

if (audit.details) {
// Turn `auditDetails.type` of undefined (LHR <4.2) and 'diagnostic' (LHR <5.0)
// into 'debugdata' (LHR ≥5.0).
// @ts-expect-error tsc rightly flags that these values shouldn't occur.
if (audit.details.type === undefined || audit.details.type === 'diagnostic') {
// @ts-expect-error details is of type never.
audit.details.type = 'debugdata';
}

// Add the jpg data URL prefix to filmstrip screenshots without them (LHR <5.0).
if (audit.details.type === 'filmstrip') {
for (const screenshot of audit.details.items) {
if (!screenshot.data.startsWith(SCREENSHOT_PREFIX)) {
screenshot.data = SCREENSHOT_PREFIX + screenshot.data;
}
}
}

// Circa 10.0, table items were refactored.
if (audit.details.type === 'table') {
for (const heading of audit.details.headings) {
/** @type {{itemType: LH.Audit.Details.ItemValueType|undefined, text: string|undefined}} */
// @ts-expect-error
const {itemType, text} = heading;
if (itemType !== undefined) {
heading.valueType = itemType;
// @ts-expect-error
delete heading.itemType;
}
if (text !== undefined) {
heading.label = text;
// @ts-expect-error
delete heading.text;
}

// @ts-expect-error
const subItemsItemType = heading.subItemsHeading?.itemType;
if (heading.subItemsHeading && subItemsItemType !== undefined) {
heading.subItemsHeading.valueType = subItemsItemType;
// @ts-expect-error
delete heading.subItemsHeading.itemType;
}
}
}

// TODO: convert printf-style displayValue.
// Added: #5099, v3
// Removed: #6767, v4
}
}
const clone = /** @type {LH.ReportResult} */ (JSON.parse(JSON.stringify(lhr)));
Util.upgradeLhrForCompatibility(clone);

// For convenience, smoosh all AuditResults into their auditRef (which has just weight & group)
if (typeof clone.categories !== 'object') throw new Error('No categories provided.');

/** @type {Map<string, Array<LH.ReportResult.AuditRef>>} */
const relevantAuditToMetricsMap = new Map();

// This backcompat converts old LHRs (<9.0.0) to use the new "hidden" group.
// Old LHRs used "no group" to identify audits that should be hidden in performance instead of the "hidden" group.
// Newer LHRs use "no group" to identify opportunities and diagnostics whose groups are assigned by details type.
const [majorVersion] = clone.lighthouseVersion.split('.').map(Number);
const perfCategory = clone.categories['performance'];
if (majorVersion < 9 && perfCategory) {
if (!clone.categoryGroups) clone.categoryGroups = {};
clone.categoryGroups['hidden'] = {title: ''};
for (const auditRef of perfCategory.auditRefs) {
if (!auditRef.group) {
auditRef.group = 'hidden';
} else if (['load-opportunities', 'diagnostics'].includes(auditRef.group)) {
delete auditRef.group;
}
}
}

for (const category of Object.values(clone.categories)) {
// Make basic lookup table for relevantAudits
category.auditRefs.forEach(metricRef => {
Expand Down Expand Up @@ -155,35 +66,6 @@ class ReportUtils {
});
}

// Add some minimal stuff so older reports still work.
if (!clone.environment) {
// @ts-expect-error
clone.environment = {benchmarkIndex: 0};
}
if (!clone.configSettings.screenEmulation) {
// @ts-expect-error
clone.configSettings.screenEmulation = {};
}
if (!clone.i18n) {
// @ts-expect-error
clone.i18n = {};
}

// In 10.0, full-page-screenshot became a top-level property on the LHR.
if (clone.audits['full-page-screenshot']) {
const details = /** @type {LH.Result.FullPageScreenshot=} */ (
clone.audits['full-page-screenshot'].details);
if (details) {
clone.fullPageScreenshot = {
screenshot: details.screenshot,
nodes: details.nodes,
};
} else {
clone.fullPageScreenshot = null;
}
delete clone.audits['full-page-screenshot'];
}

return clone;
}

Expand Down
147 changes: 0 additions & 147 deletions report/test/renderer/report-utils-test.js
Original file line number Diff line number Diff line change
Expand Up @@ -94,153 +94,6 @@ describe('util helpers', () => {
});

describe('#prepareReportResult', () => {
describe('backward compatibility', () => {
it('corrects underscored `notApplicable` scoreDisplayMode', () => {
const clonedSampleResult = JSON.parse(JSON.stringify(sampleResult));

let notApplicableCount = 0;
Object.values(clonedSampleResult.audits).forEach(audit => {
if (audit.scoreDisplayMode === 'notApplicable') {
notApplicableCount++;
audit.scoreDisplayMode = 'not_applicable';
}
});

assert.ok(notApplicableCount > 20); // Make sure something's being tested.

// Original audit results should be restored.
const preparedResult = ReportUtils.prepareReportResult(clonedSampleResult);

assert.deepStrictEqual(preparedResult.audits, sampleResult.audits);
});

it('corrects undefined auditDetails.type to `debugdata`', () => {
const clonedSampleResult = JSON.parse(JSON.stringify(sampleResult));

// Delete debugdata details types.
let undefinedCount = 0;
for (const audit of Object.values(clonedSampleResult.audits)) {
if (audit.details && audit.details.type === 'debugdata') {
undefinedCount++;
delete audit.details.type;
}
}
assert.ok(undefinedCount > 4); // Make sure something's being tested.
assert.notDeepStrictEqual(clonedSampleResult.audits, sampleResult.audits);

// Original audit results should be restored.
const preparedResult = ReportUtils.prepareReportResult(clonedSampleResult);
assert.deepStrictEqual(preparedResult.audits, sampleResult.audits);
});

it('corrects `diagnostic` auditDetails.type to `debugdata`', () => {
const clonedSampleResult = JSON.parse(JSON.stringify(sampleResult));

// Change debugdata details types.
let diagnosticCount = 0;
for (const audit of Object.values(clonedSampleResult.audits)) {
if (audit.details && audit.details.type === 'debugdata') {
diagnosticCount++;
audit.details.type = 'diagnostic';
}
}
assert.ok(diagnosticCount > 4); // Make sure something's being tested.
assert.notDeepStrictEqual(clonedSampleResult.audits, sampleResult.audits);

// Original audit results should be restored.
const preparedResult = ReportUtils.prepareReportResult(clonedSampleResult);
assert.deepStrictEqual(preparedResult.audits, sampleResult.audits);
});

it('corrects screenshots in the `filmstrip` auditDetails.type', () => {
const clonedSampleResult = JSON.parse(JSON.stringify(sampleResult));

// Strip filmstrip screenshots of data URL prefix.
let filmstripCount = 0;
for (const audit of Object.values(clonedSampleResult.audits)) {
if (audit.details && audit.details.type === 'filmstrip') {
filmstripCount++;
for (const screenshot of audit.details.items) {
screenshot.data = screenshot.data.slice('data:image/jpeg;base64,'.length);
}
}
}
assert.ok(filmstripCount > 0); // Make sure something's being tested.
assert.notDeepStrictEqual(clonedSampleResult.audits, sampleResult.audits);

// Original audit results should be restored.
const preparedResult = ReportUtils.prepareReportResult(clonedSampleResult);
assert.deepStrictEqual(preparedResult.audits, sampleResult.audits);
});

it('moves full-page-screenshot audit', () => {
const clonedSampleResult = JSON.parse(JSON.stringify(sampleResult));

clonedSampleResult.audits['full-page-screenshot'] = {
details: {
type: 'full-page-screenshot',
...sampleResult.fullPageScreenshot,
},
};
delete clonedSampleResult.fullPageScreenshot;

assert.ok(clonedSampleResult.audits['full-page-screenshot'].details.nodes); // Make sure something's being tested.
assert.notDeepStrictEqual(clonedSampleResult.audits, sampleResult.audits);

// Original audit results should be restored.
const preparedResult = ReportUtils.prepareReportResult(clonedSampleResult);
assert.deepStrictEqual(preparedResult.audits, sampleResult.audits);
assert.deepStrictEqual(preparedResult.fullPageScreenshot, sampleResult.fullPageScreenshot);
});

it('corrects performance category without hidden group', () => {
const clonedSampleResult = JSON.parse(JSON.stringify(sampleResult));

clonedSampleResult.lighthouseVersion = '8.6.0';
delete clonedSampleResult.categoryGroups['hidden'];
for (const auditRef of clonedSampleResult.categories['performance'].auditRefs) {
if (auditRef.group === 'hidden') {
delete auditRef.group;
} else if (!auditRef.group) {
auditRef.group = 'diagnostics';
}
}
assert.notDeepStrictEqual(clonedSampleResult.categories, sampleResult.categories);
assert.notDeepStrictEqual(clonedSampleResult.categoryGroups, sampleResult.categoryGroups);

// Original audit results should be restored.
const clonedPreparedResult = ReportUtils.prepareReportResult(clonedSampleResult);
const preparedResult = ReportUtils.prepareReportResult(sampleResult);
assert.deepStrictEqual(clonedPreparedResult.categories, preparedResult.categories);
assert.deepStrictEqual(clonedPreparedResult.categoryGroups, preparedResult.categoryGroups);
});

it('converts old opportunity table column headings to consolidated table headings', () => {
const clonedSampleResult = JSON.parse(JSON.stringify(sampleResult));

const auditsWithTableDetails = Object.values(clonedSampleResult.audits)
.filter(audit => audit.details?.type === 'table');
assert.notEqual(auditsWithTableDetails.length, 0);
for (const audit of auditsWithTableDetails) {
for (const heading of audit.details.headings) {
heading.itemType = heading.valueType;
heading.text = heading.label;
delete heading.valueType;
delete heading.label;

if (heading.subItemsHeading) {
heading.subItemsHeading.itemType = heading.subItemsHeading.valueType;
// @ts-expect-error
delete heading.subItemsHeading.valueType;
}
}
}

const preparedResult = ReportUtils.prepareReportResult(clonedSampleResult);
assert.deepStrictEqual(sampleResult.audits, preparedResult.audits);
});
});

it('appends stack pack descriptions to auditRefs', () => {
const clonedSampleResult = JSON.parse(JSON.stringify(sampleResult));
const iconDataURL = 'data:image/svg+xml,%3Csvg xmlns="http://www.w3.org/2000/svg"%3E%3C/svg%3E';
Expand Down
Loading

0 comments on commit 01a74a7

Please sign in to comment.