Skip to content

test: Upgrade Playwright to 1.63 and cover soft navigation web vitals - #24273

Draft
logaretm wants to merge 6 commits into
awad/inp-element-name-first-writefrom
awad/soft-nav-web-vitals-e2e
Draft

test: Upgrade Playwright to 1.63 and cover soft navigation web vitals#24273
logaretm wants to merge 6 commits into
awad/inp-element-name-first-writefrom
awad/soft-nav-web-vitals-e2e

Conversation

@logaretm

@logaretm logaretm commented Sep 9, 2026

Copy link
Copy Markdown
Member

Upgrades Playwright to 1.63 so the soft navigation web vitals path is testable at all, and adds an e2e test for it.

Three existing tests needed fixing for the newer browser:

  • INP: web-vitals defers entry processing to requestIdleCallback(..., { timeout: 1000 }), which Chromium 153 only runs on the timeout. Hiding the page after 500ms reported an unset metric. hidePage now waits for its own idle callback first.
  • CLS: the subject reloaded to finalize CLS, which raced the envelope and looped. CLS already lands on the pageload span at the idle timeout, so the reload is gone.
  • LCP: web-vitals finalizes on trusted input only, so the synthetic visibilitychange never did. It passed purely because reportAllChanges reported every entry; soft nav reporting turns that off. Now finalized with a real click.

@github-actions

github-actions Bot commented Sep 9, 2026

Copy link
Copy Markdown
Contributor

size-limit report 📦

Path Size % Change Change
@sentry/browser 28.81 kB +0.04% +11 B 🔺
@sentry/browser - with treeshaking flags 27.12 kB +0.04% +10 B 🔺
@sentry/browser - with treeshaking flags tracing without tracing 27.01 kB +0.03% +7 B 🔺
@sentry/browser (incl. Tracing) 50.21 kB +2.01% +989 B 🔺
@sentry/browser (incl. Tracing + Span Streaming) 50.23 kB +2.06% +1.01 kB 🔺
@sentry/browser (incl. Tracing, Profiling) 53.2 kB +2.06% +1.07 kB 🔺
@sentry/browser (incl. Tracing, Replay) 89.7 kB +1.07% +941 B 🔺
@sentry/browser (incl. Tracing, Replay) - with treeshaking flags 78.8 kB +1.08% +841 B 🔺
@sentry/browser (incl. Tracing, Replay with Canvas) 94.4 kB +1.03% +957 B 🔺
@sentry/browser (incl. Tracing, Replay, Feedback) 107.4 kB +0.97% +1.02 kB 🔺
@sentry/browser (incl. Feedback) 46.3 kB +0.02% +5 B 🔺
@sentry/browser (incl. sendFeedback) 33.87 kB +0.02% +6 B 🔺
@sentry/browser (incl. FeedbackAsync) 38.99 kB +0.04% +13 B 🔺
@sentry/browser (incl. Metrics) 29.84 kB +0.08% +23 B 🔺
@sentry/browser (incl. Logs) 30.1 kB +0.06% +18 B 🔺
@sentry/browser (incl. Metrics & Logs) 30.78 kB +0.11% +31 B 🔺
@sentry/react 30.57 kB +0.05% +14 B 🔺
@sentry/react (incl. Tracing) 52.54 kB +1.9% +978 B 🔺
@sentry/vue 36.07 kB +0.05% +17 B 🔺
@sentry/vue (incl. Tracing) 52.45 kB +1.9% +977 B 🔺
@sentry/svelte 28.85 kB +0.06% +15 B 🔺
CDN Bundle 30.56 kB +0.05% +15 B 🔺
CDN Bundle (incl. Tracing) 50.7 kB +1.95% +965 B 🔺
CDN Bundle (incl. Logs, Metrics) 32.83 kB +0.03% +9 B 🔺
CDN Bundle (incl. Tracing, Logs, Metrics) 52.66 kB +1.86% +959 B 🔺
CDN Bundle (incl. Replay, Logs, Metrics) 73.53 kB +0.07% +47 B 🔺
CDN Bundle (incl. Tracing, Replay) 88.24 kB +1.09% +951 B 🔺
CDN Bundle (incl. Tracing, Replay, Logs, Metrics) 90.2 kB +1.15% +1.02 kB 🔺
CDN Bundle (incl. Tracing, Replay, Feedback) 94.27 kB +1.13% +1.05 kB 🔺
CDN Bundle (incl. Tracing, Replay, Feedback, Logs, Metrics) 96.28 kB +1.14% +1.08 kB 🔺
CDN Bundle - uncompressed 90.47 kB +0.01% +8 B 🔺
CDN Bundle (incl. Tracing) - uncompressed 151.38 kB +2.17% +3.21 kB 🔺
CDN Bundle (incl. Logs, Metrics) - uncompressed 97.04 kB +0.01% +8 B 🔺
CDN Bundle (incl. Tracing, Logs, Metrics) - uncompressed 157.35 kB +2.09% +3.21 kB 🔺
CDN Bundle (incl. Replay, Logs, Metrics) - uncompressed 226.45 kB +0.08% +159 B 🔺
CDN Bundle (incl. Tracing, Replay) - uncompressed 270.95 kB +1.2% +3.19 kB 🔺
CDN Bundle (incl. Tracing, Replay, Logs, Metrics) - uncompressed 276.9 kB +1.17% +3.19 kB 🔺
CDN Bundle (incl. Tracing, Replay, Feedback) - uncompressed 284.65 kB +1.14% +3.19 kB 🔺
CDN Bundle (incl. Tracing, Replay, Feedback, Logs, Metrics) - uncompressed 290.59 kB +1.12% +3.19 kB 🔺
@sentry/nextjs (client) 54.86 kB +1.6% +860 B 🔺
@sentry/sveltekit (client) 50.65 kB +2% +992 B 🔺
@sentry/core/server 36.99 kB - -
@sentry/core/browser 13.55 kB - -
@sentry/node 127.95 kB +0.13% +158 B 🔺
@sentry/node/import (ESM hook with diagnostics-channel injection) 81.61 kB - -
@sentry/node - without tracing 88.73 kB +0.03% +22 B 🔺
@sentry/node - without channel injection 107.16 kB +0.13% +135 B 🔺
@sentry/aws-serverless 97.12 kB +0.03% +27 B 🔺
@sentry/cloudflare (withSentry) - minified 202.05 kB +0.04% +74 B 🔺
@sentry/cloudflare (withSentry) 502.91 kB +0.05% +232 B 🔺

View base workflow run

@logaretm
logaretm force-pushed the awad/soft-nav-web-vitals-e2e branch from 6b626d2 to 6bf5302 Compare September 9, 2026 22:59
@logaretm
logaretm force-pushed the awad/soft-nav-web-vitals-e2e branch from 6bf5302 to edec2c5 Compare September 9, 2026 23:18
@logaretm
logaretm added this pull request to stack #24274 September 9, 2026 23:18
…itals

Playwright 1.56 bundles Chromium 141, which has no Soft Navigations API, so
`supportsSoftNavigations()` is false and the soft navigation web vitals path never
runs under test. 1.63 bundles Chromium 153, which supports it unflagged.

Adds an e2e test asserting that a soft navigation's web vitals are attributed to the
navigation span they were measured on, through `reactRouterV6BrowserTracingIntegration`,
which starts its navigation span from a layout effect rather than from the history change.

The INP test needed a longer wait on the newer Chromium. web-vitals defers processing an
interaction's event entries to `requestIdleCallback(..., { timeout: 1000 })`; Chromium 141
ran that well inside the test's 500ms wait, Chromium 153 only runs it on the timeout. Hiding
the page before then forced a report while the metric was still unset, so no INP was emitted.
…e hiding the page

web-vitals defers processing an interaction's event entries into
`requestIdleCallback(..., { timeout: 1000 })`. Chromium 141 ran that well before the tests
hid the page; Chromium 153 only runs it once the timeout elapses, so hiding first forced a
report while the metric was still unset and no vital was emitted at all.

`hidePage` now waits for an idle callback of its own first. Idle callbacks run in scheduling
order, so one queued at hide time running means web-vitals' earlier callback has too.
CLS lands on the pageload span when it ends on the idle timeout, so nothing has to force the
page away to finalize it. The reload raced the envelope instead: on Chromium 153 the page
reloads before the in-flight send leaves, so no transaction was ever received and the test
timed out. It also looped, since the reloaded page re-ran the same subject.
web-vitals finalizes LCP on the first trusted input or visibility change and checks
`isTrusted`, so the synthetic `visibilitychange` this test dispatched never finalized it. It
passed anyway while `reportAllChanges` was on, since every LCP entry was reported as it
arrived. Soft navigation reporting turns that off, so on a browser that supports the Soft
Navigations API no LCP span was emitted at all. Clicking finalizes it the way a real user does.
`page.route` resolves with a `Disposable` as of Playwright 1.63, so returning it from a
`Promise<void>` helper no longer type-checks.

The INP tests in react-17, react-17-static and the react-router 7/8 SPA apps hid the page
500ms after the interaction, which is the same race already fixed in react-router-6:
web-vitals defers entry processing to `requestIdleCallback(..., { timeout: 1000 })` and
Chromium 153 only runs it on the timeout, so the forced report found an unset metric.

solid-tanstack-router's LCP test dispatched a synthetic `visibilitychange`, which web-vitals
ignores for finalization because it checks `isTrusted`. It passed only while
`reportAllChanges` was on. It now finalizes with a real click, as in the browser integration
suite.
Playwright 1.63 ships a WebKit that reports `deliveryType`, so the attribute is no longer
Chromium-only and the expectation moves out of the non-WebKit branch. That WebKit also
resolves the intercepted routes without measurable delay, so the TTFB range check keeps its
upper bound (which is what verifies we report seconds, not milliseconds) but only holds the
other engines to a non-zero value.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant