From a47bdd13501a015128d11411e0a2dc7ebcde4ca1 Mon Sep 17 00:00:00 2001 From: Andrew Hyndman Date: Fri, 31 Mar 2023 14:10:42 -0700 Subject: [PATCH] Capture navigationType for the measurement in question --- README.md | 12 ++++++++++++ src/index.ts | 2 +- src/visuallyCompleteCalculator.ts | 26 +++++++++++++++++++++++++- test/e2e/bfcache/index.spec.ts | 3 +++ 4 files changed, 41 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 4d7fca1..2ea79b0 100644 --- a/README.md +++ b/README.md @@ -191,6 +191,18 @@ export type Metric = { // (this can be either a mutation or a load event target, whichever // occurred last) lastVisibleChange?: HTMLElement | TimestampedMutationRecord; + + // describes how the navigation being measured was initiated + navigationType: // Navigation started by clicking a link, entering the URL in the browser's address bar or form submission. + | 'navigate' + // Navigation is through the browser's reload operation. + | 'reload' + // Navigation is through the browser's history traversal operation. + | 'back_forward' + // Navigation is initiated by a prerender hint. + | 'prerender' + // Navigation was triggered with a script operation, e.g. in a single page application. + | 'script'; }; }; ``` diff --git a/src/index.ts b/src/index.ts index 3acba5f..7cc1d52 100644 --- a/src/index.ts +++ b/src/index.ts @@ -31,7 +31,7 @@ export const init = (options?: TtvcOptions) => { window.addEventListener('pageshow', (event) => { // abort if this is the initial pageload if (!event.persisted) return; - void calculator.start(event.timeStamp); + void calculator.start(event.timeStamp, true); }); }; diff --git a/src/visuallyCompleteCalculator.ts b/src/visuallyCompleteCalculator.ts index 9ae0c56..8f25544 100644 --- a/src/visuallyCompleteCalculator.ts +++ b/src/visuallyCompleteCalculator.ts @@ -4,6 +4,18 @@ import {requestAllIdleCallback} from './requestAllIdleCallback'; import {InViewportImageObserver} from './inViewportImageObserver'; import {Logger} from './util/logger'; +export type NavigationType = + // Navigation started by clicking a link, entering the URL in the browser's address bar or form submission. + | 'navigate' + // Navigation is through the browser's reload operation. + | 'reload' + // Navigation is through the browser's history traversal operation. + | 'back_forward' + // Navigation is initiated by a prerender hint. + | 'prerender' + // Navigation was triggered with a script operation, e.g. in a single page application. + | 'script'; + export type Metric = { // time since timeOrigin that the navigation was triggered // (this will be 0 for the initial pageload) @@ -22,6 +34,8 @@ export type Metric = { // the most recent visual update; this can be either a mutation or a load event target lastVisibleChange?: HTMLElement | TimestampedMutationRecord; + + navigationType: NavigationType; }; }; @@ -82,7 +96,7 @@ class VisuallyCompleteCalculator { } /** begin measuring a new navigation */ - async start(start = 0) { + async start(start = 0, isBfCacheRestore = false) { const navigationIndex = (this.navigationCount += 1); this.activeMeasurementIndex = navigationIndex; Logger.info('VisuallyCompleteCalculator.start()'); @@ -115,12 +129,22 @@ class VisuallyCompleteCalculator { // identify timestamp of last visible change const end = Math.max(start, this.lastImageLoadTimestamp, this.lastMutation?.timestamp ?? 0); + const navigationEntries = performance.getEntriesByType( + 'navigation' + ) as PerformanceNavigationTiming[]; + const navigationType = isBfCacheRestore + ? 'back_forward' + : start !== 0 + ? 'script' + : navigationEntries[navigationEntries.length - 1].type; + // report result to subscribers this.next({ start, end, duration: end - start, detail: { + navigationType, didNetworkTimeOut, lastVisibleChange: this.lastImageLoadTimestamp > (this.lastMutation?.timestamp ?? 0) diff --git a/test/e2e/bfcache/index.spec.ts b/test/e2e/bfcache/index.spec.ts index dbae532..7b12f1a 100644 --- a/test/e2e/bfcache/index.spec.ts +++ b/test/e2e/bfcache/index.spec.ts @@ -16,6 +16,7 @@ test.describe('TTVC', () => { expect(entries.length).toBe(1); expect(entries[0].duration).toBeGreaterThanOrEqual(PAGELOAD_DELAY); expect(entries[0].duration).toBeLessThanOrEqual(PAGELOAD_DELAY + FUDGE); + expect(entries[0].detail.navigationType).toBe('navigate'); await page.goto(`/test/bfcache/about?delay=${PAGELOAD_DELAY}&cache=true`, { waitUntil: 'networkidle', @@ -26,6 +27,7 @@ test.describe('TTVC', () => { expect(entries.length).toBe(1); expect(entries[0].duration).toBeGreaterThanOrEqual(PAGELOAD_DELAY); expect(entries[0].duration).toBeLessThanOrEqual(PAGELOAD_DELAY + FUDGE); + expect(entries[0].detail.navigationType).toBe('navigate'); await page.goBack({waitUntil: 'networkidle'}); @@ -34,5 +36,6 @@ test.describe('TTVC', () => { // note: webkit clears previous values from this list on page restore expect(entries[entries.length - 1].duration).toBeGreaterThanOrEqual(0); expect(entries[entries.length - 1].duration).toBeLessThanOrEqual(FUDGE); + expect(entries[entries.length - 1].detail.navigationType).toBe('back_forward'); }); });