From b3887f11fb123a4ba067f843f1b2ce740c6c8f23 Mon Sep 17 00:00:00 2001 From: Traven Reese Date: Wed, 1 Jul 2026 19:19:10 +0000 Subject: [PATCH] core(baseline): report newest feature summary in displayValue --- core/audits/baseline.js | 93 +++++++++++++++++++------------ core/test/audits/baseline-test.js | 52 +++++++++++++++++ core/test/results/sample_v2.json | 55 +++++++++--------- 3 files changed, 138 insertions(+), 62 deletions(-) diff --git a/core/audits/baseline.js b/core/audits/baseline.js index 9d91d84e0cfa..ec0d11870d1f 100644 --- a/core/audits/baseline.js +++ b/core/audits/baseline.js @@ -91,6 +91,25 @@ class Baseline extends Audit { return null; } + /** + * @param {string} featureId + * @param {{high: Record, low: Record, limited: string[]}} featureData + * @return {string|null} + */ + static getLowDate(featureId, featureData) { + if (featureId in featureData.low) { + const lowData = /** @type {Record} */ (featureData.low); + return lowData[featureId]; + } + if (featureId in featureData.high) { + const highData = /** @type {Record} */ (featureData.high); + const date = new Date(highData[featureId]); + date.setUTCMonth(date.getUTCMonth() - 30); + return date.toISOString().slice(0, 10); + } + return null; + } + /** * @param {LH.Artifacts} artifacts * @return {Promise} @@ -101,15 +120,14 @@ class Baseline extends Audit { /** @type {Map} */ const featuresMap = new Map(); - const dxEvents = /** @type {DXFeatureEvent[]} */ ( - (trace.traceEvents || []).filter(e => e.cat === 'blink.webdx_feature_usage' && - e.args?.feature) - ); - - for (const event of dxEvents) { - const key = `${event.args.feature}`; + for (const e of trace.traceEvents || []) { + if (e.cat !== 'blink.webdx_feature_usage' || !e.args?.feature) { + continue; + } + const event = /** @type {DXFeatureEvent} */ (e); - if (featuresMap.has(key)) continue; + const feature = /** @type {string} */ (event.args.feature); + if (featuresMap.has(feature)) continue; /** @type {LH.Audit.Details.SourceLocationValue | undefined} */ let source; @@ -121,8 +139,8 @@ class Baseline extends Audit { source = Audit.makeSourceLocation(event.args.url, line, column); } - featuresMap.set(key, { - featureId: event.args.feature, + featuresMap.set(feature, { + featureId: feature, source, }); } @@ -142,6 +160,8 @@ class Baseline extends Audit { if (!status) continue; const {displayStatus, baselineTier} = status; + const lowDate = Baseline.getLowDate(featureId, Baseline.featureData) || ''; + baselineStatus.push({ featureId: { type: /** @type {const} */ ('link'), @@ -154,11 +174,12 @@ class Baseline extends Audit { displayString: displayStatus, }, source: feature.source, + lowDate, }); } /** @type {LH.Audit.Details.Table['headings']} */ - const headings = [ + const webFeatureHeadings = [ { key: 'featureId', valueType: 'link', @@ -176,46 +197,48 @@ class Baseline extends Audit { }, ]; - /** - * Determines the sorting rank of a baseline status. - * @param {string} status The display status string. - * @return {number} The numerical rank (1 is the highest priority). - */ - const getStatusRank = (status) => { - if (status.startsWith('Limited')) { - return 1; - } - if (status.startsWith('Newly')) { - return 2; - } - if (status.startsWith('Widely')) { - return 3; - } - return 4; + /** @type {Record} */ + const TIER_RANKS = { + limited: 1, + low: 2, + high: 3, }; const sortedStatuses = baselineStatus.sort((featureA, featureB) => { - const rankA = getStatusRank(featureA.displayStatus.displayString); - const rankB = getStatusRank(featureB.displayStatus.displayString); + const rankA = TIER_RANKS[featureA.displayStatus.status] || 4; + const rankB = TIER_RANKS[featureB.displayStatus.status] || 4; if (rankA !== rankB) { return rankA - rankB; } - const hasSourceA = !!featureA.source; - const hasSourceB = !!featureB.source; + return featureB.lowDate.localeCompare(featureA.lowDate); + }); - if (hasSourceA !== hasSourceB) { - return hasSourceA ? -1 : 1; + const hasLimited = baselineStatus.some(item => item.displayStatus.status === 'limited'); + + let displayValue; + if (!hasLimited && sortedStatuses.length > 0) { + const newestFeature = sortedStatuses[0]; + const featureName = newestFeature.featureId.text; + const lowDate = newestFeature.lowDate; + if (lowDate) { + const year = lowDate.substring(0, 4); + displayValue = `Baseline ${year} based on ${featureName} (${lowDate})`; } + } - return 0; + // Remove `lowDate` property from items before generating details table. + const tableItems = sortedStatuses.map(item => { + const {lowDate: _, ...rest} = item; + return rest; }); - const details = Audit.makeTableDetails(headings, sortedStatuses); + const details = Audit.makeTableDetails(webFeatureHeadings, tableItems); return { score: 1, + displayValue, details, }; } diff --git a/core/test/audits/baseline-test.js b/core/test/audits/baseline-test.js index a84fbc8b4d9c..7bfe20398eda 100644 --- a/core/test/audits/baseline-test.js +++ b/core/test/audits/baseline-test.js @@ -194,6 +194,58 @@ describe('Baseline Audit', () => { }); }); + it('should not set displayValue when a limited availability feature ' + + 'is present (newest is newly available)', async () => { + const traceEvents = [ + {args: {feature: 'accelerometer'}, cat: 'blink.webdx_feature_usage'}, + { + args: {feature: 'abortsignal-any'}, // low (2024-03-19) + cat: 'blink.webdx_feature_usage', + }, + {args: {feature: 'forced-colors'}, cat: 'blink.webdx_feature_usage'}, // high (2022-09-12) + ]; + const result = await Baseline.audit({Trace: {traceEvents}}); + expect(result.displayValue).toBeUndefined(); + }); + + it('should not set displayValue when a limited availability feature ' + + 'is present (newest is widely available)', async () => { + const traceEvents = [ + {args: {feature: 'accelerometer'}, cat: 'blink.webdx_feature_usage'}, + {args: {feature: 'forced-colors'}, cat: 'blink.webdx_feature_usage'}, // high (2022-09-12) + ]; + const result = await Baseline.audit({Trace: {traceEvents}}); + expect(result.displayValue).toBeUndefined(); + }); + + it('should not set displayValue when a limited availability feature ' + + 'is present (only limited)', async () => { + const traceEvents = [ + {args: {feature: 'accelerometer'}, cat: 'blink.webdx_feature_usage'}, + ]; + const result = await Baseline.audit({Trace: {traceEvents}}); + expect(result.displayValue).toBeUndefined(); + }); + + it('should set correct displayValue when no limited availability features ' + + 'are present (newest is newly available)', async () => { + const traceEvents = [ + {args: {feature: 'abortsignal-any'}, cat: 'blink.webdx_feature_usage'}, // low (2024-03-19) + {args: {feature: 'forced-colors'}, cat: 'blink.webdx_feature_usage'}, // high (2022-09-12) + ]; + const result = await Baseline.audit({Trace: {traceEvents}}); + expect(result.displayValue).toEqual('Baseline 2024 based on abortsignal-any (2024-03-19)'); + }); + + it('should set correct displayValue when no limited availability features ' + + 'are present (newest is widely available)', async () => { + const traceEvents = [ + {args: {feature: 'forced-colors'}, cat: 'blink.webdx_feature_usage'}, // high (2022-09-12) + ]; + const result = await Baseline.audit({Trace: {traceEvents}}); + expect(result.displayValue).toEqual('Baseline 2020 based on forced-colors (2020-03-12)'); + }); + describe('getFeatureStatus', () => { const fakeData = { high: { diff --git a/core/test/results/sample_v2.json b/core/test/results/sample_v2.json index d26b225d9ede..436e8a418e23 100644 --- a/core/test/results/sample_v2.json +++ b/core/test/results/sample_v2.json @@ -4335,6 +4335,7 @@ "description": "Lists web features used on the page and their Baseline status as of 2026-06-19. [Learn more about Baseline](https://webstatus.dev/).", "score": 1, "scoreDisplayMode": "informative", + "displayValue": "Baseline 2024 based on fetch-priority (2024-10-29)", "details": { "type": "table", "headings": [ @@ -4370,20 +4371,25 @@ { "featureId": { "type": "link", - "text": "request-animation-frame", - "url": "https://webstatus.dev/features/request-animation-frame" + "text": "not", + "url": "https://webstatus.dev/features/not" }, "displayStatus": { "type": "baseline-status", "status": "high", - "displayString": "Widely Available (2015-07-29)" + "displayString": "Widely Available (2021-01-21)" + } + }, + { + "featureId": { + "type": "link", + "text": "slot", + "url": "https://webstatus.dev/features/slot" }, - "source": { - "type": "source-location", - "url": "http://localhost:10200/dobetterweb/dbw_tester.html", - "urlProvider": "network", - "line": 412, - "column": 2 + "displayStatus": { + "type": "baseline-status", + "status": "high", + "displayString": "Widely Available (2020-01-15)" } }, { @@ -4405,18 +4411,6 @@ "column": 23 } }, - { - "featureId": { - "type": "link", - "text": "template", - "url": "https://webstatus.dev/features/template" - }, - "displayStatus": { - "type": "baseline-status", - "status": "high", - "displayString": "Widely Available (2015-11-12)" - } - }, { "featureId": { "type": "link", @@ -4432,25 +4426,32 @@ { "featureId": { "type": "link", - "text": "slot", - "url": "https://webstatus.dev/features/slot" + "text": "template", + "url": "https://webstatus.dev/features/template" }, "displayStatus": { "type": "baseline-status", "status": "high", - "displayString": "Widely Available (2020-01-15)" + "displayString": "Widely Available (2015-11-12)" } }, { "featureId": { "type": "link", - "text": "not", - "url": "https://webstatus.dev/features/not" + "text": "request-animation-frame", + "url": "https://webstatus.dev/features/request-animation-frame" }, "displayStatus": { "type": "baseline-status", "status": "high", - "displayString": "Widely Available (2021-01-21)" + "displayString": "Widely Available (2015-07-29)" + }, + "source": { + "type": "source-location", + "url": "http://localhost:10200/dobetterweb/dbw_tester.html", + "urlProvider": "network", + "line": 412, + "column": 2 } } ]