From 5d5f96276276f7fd28042e13ffd63f6940c99662 Mon Sep 17 00:00:00 2001 From: KS Date: Thu, 29 Oct 2026 14:56:54 -0400 Subject: [PATCH] Add Fenix navigation benchmark data --- src/lib/navbench/config.test.ts | 37 ++++++++++++++++ src/lib/navbench/config.ts | 74 ++++++++++++++++++++++++++++---- src/lib/navbench/data.ts | 11 ++++- src/routes/navbench/+page.svelte | 27 +++++++++--- 4 files changed, 131 insertions(+), 18 deletions(-) diff --git a/src/lib/navbench/config.test.ts b/src/lib/navbench/config.test.ts index 5c660a6..6fa2764 100644 --- a/src/lib/navbench/config.test.ts +++ b/src/lib/navbench/config.test.ts @@ -3,7 +3,10 @@ import { annotationsFor, displayName, OVERALL_TEST, + proseList, + scenariosIn, signatureTest, + sitesIn, testKey, videoScenario } from './config'; @@ -80,3 +83,37 @@ describe('annotationsFor', () => { expect(annotationsFor(OVERALL_TEST, 'osxm4', [windowsOnly])).toEqual([]); }); }); + +describe('the explainer lists', () => { + const tests = [ + OVERALL_TEST, + 'wikipedia-nav-subnav-score', + 'amazon-nav-load-score', + 'amazon-nav-subnav-score', + 'google-docs-nav-warm-score' + ]; + + it('names each site once, alphabetically, without the overall', () => { + expect(sitesIn(tests)).toEqual(['Amazon', 'Google Docs', 'Wikipedia']); + }); + + it('names each scenario once, in a fixed order', () => { + expect(scenariosIn(tests)).toEqual(['page load', 'sub-navigation', 'warm-load']); + expect(scenariosIn(['amazon-nav-subnav-score', 'amazon-nav-load-score'])).toEqual([ + 'page load', + 'sub-navigation' + ]); + }); + + it('is empty before any tests have loaded', () => { + expect(sitesIn([OVERALL_TEST])).toEqual([]); + expect(scenariosIn([OVERALL_TEST])).toEqual([]); + }); + + it('joins items without a serial comma', () => { + expect(proseList([])).toBe(''); + expect(proseList(['A'])).toBe('A'); + expect(proseList(['A', 'B'])).toBe('A and B'); + expect(proseList(['A', 'B', 'C'])).toBe('A, B and C'); + }); +}); diff --git a/src/lib/navbench/config.ts b/src/lib/navbench/config.ts index 4c7e2db..ebaee84 100644 --- a/src/lib/navbench/config.ts +++ b/src/lib/navbench/config.ts @@ -7,8 +7,12 @@ export const NAVBENCH_FRAMEWORK = FRAMEWORK.browsertime; export const NAVBENCH_REPOSITORY = 'autoland' as const; -/** The desktop platforms the benchmark runs on, from the shared platform table. */ -const NAVBENCH_PLATFORM_KEYS = ['linux', 'osxm4', 'windows', 'windows-hwref']; +/** + * The platforms the benchmark runs on, from the shared platform table. Android + * runs the mobile variant (nav-bench-m) in Fenix, which reports under the same + * suite name. + */ +const NAVBENCH_PLATFORM_KEYS = ['linux', 'osxm4', 'windows', 'windows-hwref', 'android-a55']; export const NAVBENCH_PLATFORMS: readonly PlatformConfig[] = PLATFORMS.filter((platform) => NAVBENCH_PLATFORM_KEYS.includes(platform.key) @@ -52,21 +56,73 @@ const SITE_LABELS: Record = { const capitalize = (word: string) => word.charAt(0).toUpperCase() + word.slice(1); +/** + * A test's site and scenario: `google-docs` and `warm` from + * `google-docs-nav-warm-score`. + * + * Split at `-nav-` rather than at the first hyphen, so a hyphenated site name + * stays together. + */ +function parseTest(test: string): { site: string; scenario?: string } { + const name = test.replace(/-score$/, ''); + const split = name.indexOf('-nav-'); + return split === -1 + ? { site: name } + : { site: name.slice(0, split), scenario: name.slice(split + '-nav-'.length) }; +} + +const siteLabel = (site: string) => SITE_LABELS[site] ?? site.split('-').map(capitalize).join(' '); + /** * `BBC nav subnav` from `bbc-nav-subnav-score`. * * Every test in this suite is a score, so the suffix carries no information in - * a table whose header already says so. Split at `-nav-` rather than at the - * first hyphen, so `google-docs-nav-warm-score` is Google Docs and not Google. + * a table whose header already says so. */ export function displayName(test: string): string { if (test === OVERALL_TEST) return 'Overall Score'; - const name = test.replace(/-score$/, ''); - const split = name.indexOf('-nav-'); - const site = split === -1 ? name : name.slice(0, split); - const label = SITE_LABELS[site] ?? site.split('-').map(capitalize).join(' '); - return split === -1 ? label : `${label} nav ${name.slice(split + '-nav-'.length)}`; + const { site, scenario } = parseTest(test); + return scenario === undefined ? siteLabel(site) : `${siteLabel(site)} nav ${scenario}`; +} + +/** The sites among these tests, labelled and alphabetical. */ +export function sitesIn(tests: readonly string[]): string[] { + const sites = new Set( + tests.filter((test) => test !== OVERALL_TEST).map((test) => siteLabel(parseTest(test).site)) + ); + return [...sites].sort((a, b) => a.localeCompare(b)); +} + +const SCENARIO_LABELS: Record = { + load: 'page load', + subnav: 'sub-navigation', + warm: 'warm-load' +}; + +/** The scenarios among these tests, in prose, in the order above. */ +export function scenariosIn(tests: readonly string[]): string[] { + const scenarios = new Set( + tests.flatMap((test) => { + const { scenario } = parseTest(test); + return test === OVERALL_TEST || scenario === undefined ? [] : [scenario]; + }) + ); + const known = Object.keys(SCENARIO_LABELS); + return [...scenarios] + .sort((a, b) => { + const rank = (scenario: string) => + known.includes(scenario) ? known.indexOf(scenario) : known.length; + return rank(a) - rank(b) || a.localeCompare(b); + }) + .map((scenario) => SCENARIO_LABELS[scenario] ?? scenario); +} + +/** `a, b and c`, matching the explainer's house style (no serial comma). */ +export function proseList(items: readonly string[]): string { + return items.length <= 1 + ? (items[0] ?? '') + : `${items.slice(0, -1).join(', ')} and ${items[items.length - 1]}`; } /** diff --git a/src/lib/navbench/data.ts b/src/lib/navbench/data.ts index 333c934..36006b5 100644 --- a/src/lib/navbench/data.ts +++ b/src/lib/navbench/data.ts @@ -25,7 +25,10 @@ import { /** Days of history behind the comparison table, independent of the chart range. */ export const TABLE_WINDOW_DAYS = 30; -/** Every nav-bench signature on a platform, both Firefox and Nightly-as-Release. */ +/** + * Every nav-bench signature on a platform, both Firefox and Nightly-as-Release. + * On Android the application is Fenix rather than Firefox. + */ export async function loadNavBenchSignatures( platformKey: string, signal?: AbortSignal @@ -47,7 +50,11 @@ export async function loadNavBenchSignatures( const navBench = perPlatform .flat() - .filter((sig) => sig.suite === NAVBENCH_SUITE && sig.application === 'firefox'); + .filter( + (sig) => + sig.suite === NAVBENCH_SUITE && + (sig.application === 'firefox' || sig.application === 'fenix') + ); return selectCanonicalSignatures(navBench); } diff --git a/src/routes/navbench/+page.svelte b/src/routes/navbench/+page.svelte index 0f12995..a9d7d9d 100644 --- a/src/routes/navbench/+page.svelte +++ b/src/routes/navbench/+page.svelte @@ -15,7 +15,11 @@ NAVBENCH_FRAMEWORK, NAVBENCH_PLATFORMS, NAVBENCH_REPOSITORY, - OVERALL_TEST + OVERALL_TEST, + platformByKey, + proseList, + scenariosIn, + sitesIn } from '$lib/navbench/config'; import { loadNavBenchMeasurements, @@ -56,6 +60,14 @@ const tests = $derived(testsIn(signatures.value ?? [])); + // The explainer describes what runs on this platform: Android runs fewer + // sites and scenarios than desktop. Empty until the signatures load. + const browserName = $derived( + platformByKey(os).group === 'mobile' ? 'Firefox for Android' : 'Firefox' + ); + const sites = $derived(proseList(sitesIn(tests))); + const scenarios = $derived(proseList(scenariosIn(tests))); + const chartData = resource(async (signal) => { const all = signatures.value; if (!all) return []; @@ -204,12 +216,13 @@
- Navigation Benchmark tests Firefox navigation performance across real websites: - Amazon, BBC, DuckDuckGo, Facebook, Google, Google Docs, Reddit, Wikipedia and Yahoo. Sites are - tested across a mix of page load, sub-navigation and warm-load scenarios, scored using - SpeedIndex. Per-site and overall scores are geometric means; higher is better. Click a point to - play the screen recordings that run made. Dashed markers on the charts flag benchmark changes; - hover for details, or click the label to open the bug. + Navigation Benchmark tests {browserName} navigation performance across real websites{#if sites}: + {sites}{/if}.{' '}{#if scenarios} + Sites are tested across a mix of {scenarios} scenarios, scored using SpeedIndex.{:else} + Sites are scored using SpeedIndex.{/if} + Per-site and overall scores are geometric means; higher is better. Click a point to play the screen + recordings that run made. Dashed markers on the charts flag benchmark changes; hover for details, + or click the label to open the bug.