Skip to content

Add e2e test for stale use cache content served to cookieless requests in dev#95931

Draft
gaojude wants to merge 1 commit into
canaryfrom
worktree-use-cache-dev-staleness-test
Draft

Add e2e test for stale use cache content served to cookieless requests in dev#95931
gaojude wants to merge 1 commit into
canaryfrom
worktree-use-cache-dev-staleness-test

Conversation

@gaojude

@gaojude gaojude commented Jul 19, 2026

Copy link
Copy Markdown
Contributor

What?

Adds a fourth dev-mode case to the use-cache-dev e2e suite. It proves that next dev keeps serving pre-edit use cache content to requests that carry no HMR refresh hash cookie, and that a hard reload is what repairs the entry.

Why?

In dev, editing a file does not evict existing use cache entries. Invalidation works by re-keying: the HMR client stores each server component change hash in a __next_hmr_refresh_hash__ cookie, and requests that carry the cookie use a new cache key. A client without the cookie (curl, a fresh browser profile, any plain HTTP request) keeps reading the old entry until it expires.

The suite covers the browser side of this (HMR updates, soft reload, hard reload) but had no coverage for what a cookieless client sees after an edit. That gap is easy to mistake for a bug: compilation is clean and the connected browser shows the edit, yet curl http://localhost:3000/ still returns the old HTML.

How?

The test drives the scenario with plain HTTP requests against the existing fixture, whose cached page renders the string foo:

  1. A request without any cookie renders foo and creates the cache entry keyed without an HMR refresh hash.
  2. next.patchFile replaces foo with baz in app/page.tsx.
  3. A request with the header cookie: __next_hmr_refresh_hash__=<random> renders baz once compilation finishes. Each retry attempt uses a fresh random value; a fixed value would cache a pre-edit render under that key on the first attempt and never converge. This request is also the compile barrier, so the next assertion cannot race.
  4. A plain request still renders foo. This is the staleness claim: the edit did not evict the cookieless entry.
  5. A request with the header Cache-Control: no-cache (what a browser hard reload sends) renders baz and writes the fresh result back.
  6. A final plain request renders baz. The entry is repaired for every client from here on.

After the patch auto-reverts, one more Cache-Control: no-cache request re-syncs the entry to foo so later tests in the suite start from a clean state.

Passes with both pnpm test-dev-turbo and pnpm test-dev-webpack. The existing should successfully finish compilation when "use cache" directive is added/removed case fails on my machine on unmodified canary as well, so that failure is unrelated to this change.

@github-actions

github-actions Bot commented Jul 19, 2026

Copy link
Copy Markdown
Contributor

Stats from current PR

🔴 3 regressions

Metric Canary PR Change Trend
Webpack Build Time (cached) 25.578s 26.313s 🔴 +735ms (+3%) ▁▁█▁▁
Webpack Cold (First Request) 3.423s 3.552s 🔴 +129ms (+4%) ▁▁█▂▁
Webpack Warm (First Request) 3.419s 3.546s 🔴 +127ms (+4%) ▁▁█▁▁
📊 All Metrics
📖 Metrics Glossary

Dev Server Metrics:

  • Listen = TCP port starts accepting connections
  • First Request = HTTP server returns successful response
  • Cold = Fresh build (no cache)
  • Warm = With cached build artifacts

Build Metrics:

  • Fresh = Clean build (no .next directory)
  • Cached = With existing .next directory

Change Thresholds:

  • Time: Changes < 50ms AND < 10%, OR < 2% are insignificant
  • Size: Changes < 1KB AND < 1% are insignificant
  • All other changes are flagged to catch regressions

⚡ Dev Server

Metric Canary PR Change Trend
Cold (Listen) 1.064s 1.065s ▁▂█▂▂
Cold (Ready in log) 1.030s 1.050s ▁▁█▂▁
Cold (First Request) 1.725s 1.734s ▁▁█▃▁
Warm (Listen) 1.065s 1.064s ▁▂▇▂▂
Warm (Ready in log) 1.039s 1.034s ▁▁█▂▁
Warm (First Request) 807ms 801ms ▁▁█▃▁
📦 Dev Server (Webpack) (Legacy)

📦 Dev Server (Webpack)

Metric Canary PR Change Trend
Cold (Listen) 812ms 813ms ▂▂█▂▁
Cold (Ready in log) 782ms 802ms ▂▁█▂▁
Cold (First Request) 3.423s 3.552s 🔴 +129ms (+4%) ▁▁█▂▁
Warm (Listen) 812ms 814ms ▂▂█▂▁
Warm (Ready in log) 783ms 806ms ▂▁█▂▁
Warm (First Request) 3.419s 3.546s 🔴 +127ms (+4%) ▁▁█▁▁

⚡ Production Builds

Metric Canary PR Change Trend
Fresh Build 7.056s 7.131s ▁▁█▃▁
Cached Build 7.136s 7.281s ▁▁█▄▁
📦 Production Builds (Webpack) (Legacy)

📦 Production Builds (Webpack)

Metric Canary PR Change Trend
Fresh Build 26.467s 26.351s ▁▁█▁▁
Cached Build 25.578s 26.313s 🔴 +735ms (+3%) ▁▁█▁▁
node_modules Size 517 MB 517 MB ▁▁▁▁▁
📦 Bundle Sizes

Bundle Sizes

⚡ Turbopack

Client

Main Bundles
Canary PR Change
03gmyxr7dukvs.js gzip 10.3 kB N/A -
040t1q17lo593.js gzip 8.77 kB N/A -
0bb0vtp8dwsez.js gzip 450 B N/A -
0bc2qmapf2-xp.js gzip 65.6 kB N/A -
0cz1d0mv5g_q7.js gzip 39.4 kB 39.4 kB
0eitupjbfrykc.js gzip 152 B N/A -
0erxj74x8k6ec.js gzip 168 B N/A -
0gtdybiz8dm6u.js gzip 157 B N/A -
0knlni7gtnkvn.js gzip 157 B N/A -
0l0sr6gau5xjn.js gzip 3.53 kB N/A -
0nxqz3q165m2l.js gzip 220 B N/A -
0qr-1tnb7_hg5.js gzip 5.72 kB N/A -
0ri4somsc4db7.js gzip 157 B N/A -
0uilzvklme4ea.js gzip 8.76 kB N/A -
0xt5c-i2o_3vb.js gzip 157 B N/A -
0z0y83mfx-l-h.js gzip 10 kB N/A -
1_-g_r52k540c.js gzip 7.4 kB N/A -
1292my3koxtzp.js gzip 156 B N/A -
15ok8ydf274z0.js gzip 10.6 kB N/A -
16cqqcigykfea.js gzip 13.6 kB N/A -
17h18zxt1915l.js gzip 1.46 kB N/A -
1elt1qium-r2m.css gzip 115 B 115 B
1fq1h7dxad4cv.js gzip 9.44 kB N/A -
1fym-adbk_69_.js gzip 13.1 kB N/A -
1gav7nfh4y8ln.js gzip 8.76 kB N/A -
1gbsj2syj4hxg.js gzip 155 B N/A -
1hve002w_oi_4.js gzip 162 B N/A -
1o34vh44ndvtu.js gzip 13.2 kB N/A -
2pv--9ujiu0_m.js gzip 162 B N/A -
2rypqffktcxqh.js gzip 8.69 kB N/A -
2uqsizxtehgqf.js gzip 8.79 kB N/A -
2uu-v0yf5py2b.js gzip 152 B N/A -
2vzktdqwbijqc.js gzip 8.74 kB N/A -
329uye84qackw.js gzip 154 B N/A -
36648vv6scsxd.js gzip 156 B N/A -
3c244akvx_-be.js gzip 8.74 kB N/A -
3dmlemitx3ef3.js gzip 2.29 kB N/A -
3vfl1fxa4me-g.js gzip 45.2 kB N/A -
3xj5qsn_k_7op.js gzip 71 kB N/A -
4237vdj6eyslt.js gzip 8.7 kB N/A -
turbopack-06..a61-.js gzip 3.8 kB N/A -
turbopack-0f..q0ul.js gzip 3.8 kB N/A -
turbopack-0j..z5ci.js gzip 3.8 kB N/A -
turbopack-0s..7q--.js gzip 3.8 kB N/A -
turbopack-0s..fq_h.js gzip 3.8 kB N/A -
turbopack-18..bs07.js gzip 3.8 kB N/A -
turbopack-1c..65_4.js gzip 3.8 kB N/A -
turbopack-2l..he8o.js gzip 3.8 kB N/A -
turbopack-2m..eii_.js gzip 3.8 kB N/A -
turbopack-2r..tnn7.js gzip 3.8 kB N/A -
turbopack-2u..7enc.js gzip 3.78 kB N/A -
turbopack-32..48ct.js gzip 3.82 kB N/A -
turbopack-3n..nnos.js gzip 3.8 kB N/A -
turbopack-3u..6j97.js gzip 3.8 kB N/A -
00tc16j5-eos9.js gzip N/A 1.46 kB -
05p6p41p1-nn4.js gzip N/A 156 B -
0anw1-lx1rplr.js gzip N/A 157 B -
0gaw1gmpwuwkn.js gzip N/A 8.8 kB -
0k_p4mfk_qsc2.js gzip N/A 154 B -
0n68kwq6s1xjy.js gzip N/A 156 B -
0nk_trkwu_2tz.js gzip N/A 155 B -
0uhz4zhjb7hwp.js gzip N/A 156 B -
12zyjjmq4oad1.js gzip N/A 5.72 kB -
18j5_52co3scg.js gzip N/A 156 B -
1k9cc4-gpa2cx.js gzip N/A 13.1 kB -
1kfiwrcr725kl.js gzip N/A 13.2 kB -
1n59uogejo81s.js gzip N/A 10.3 kB -
1npei3hlexdv3.js gzip N/A 9.99 kB -
1ssuiwj_rnpc5.js gzip N/A 8.77 kB -
1v9l3xqke-2mc.js gzip N/A 155 B -
2-phc9obd3nyd.js gzip N/A 8.77 kB -
24utq57r3l1cw.js gzip N/A 8.7 kB -
25tly5nztb7d5.js gzip N/A 65.6 kB -
28asog09z62lv.js gzip N/A 155 B -
2fhjq6ek3srmr.js gzip N/A 8.69 kB -
2g0t7upz_v-hf.js gzip N/A 10.6 kB -
2h5nkoal80pda.js gzip N/A 45.2 kB -
2ia9qqy99aie9.js gzip N/A 13.6 kB -
2n5fp99a5xt9s.js gzip N/A 7.4 kB -
2oza28kdqvoay.js gzip N/A 8.74 kB -
2zh8-mmhujy8o.js gzip N/A 2.29 kB -
305y0a1-4hhz6.js gzip N/A 166 B -
30h_m0irj-q6p.js gzip N/A 221 B -
37bp97n04vfcu.js gzip N/A 153 B -
37ejbfx1u9lpo.js gzip N/A 8.75 kB -
38c63ct4i1cy7.js gzip N/A 449 B -
3jimpal3lvz5e.js gzip N/A 9.44 kB -
3su6a4l8jbfth.js gzip N/A 3.53 kB -
3wb4pe4z4-aim.js gzip N/A 8.77 kB -
3wsb05-fi2hd9.js gzip N/A 161 B -
405raob-lmvse.js gzip N/A 160 B -
42a5ce6ht4_fn.js gzip N/A 71 kB -
turbopack-02..ogf1.js gzip N/A 3.8 kB -
turbopack-08..uf5w.js gzip N/A 3.8 kB -
turbopack-10..7r_e.js gzip N/A 3.8 kB -
turbopack-1c..ez2a.js gzip N/A 3.8 kB -
turbopack-1e..rzgu.js gzip N/A 3.8 kB -
turbopack-1e.._44u.js gzip N/A 3.8 kB -
turbopack-1l..7b9n.js gzip N/A 3.8 kB -
turbopack-1q..j8nv.js gzip N/A 3.78 kB -
turbopack-1z..3w23.js gzip N/A 3.81 kB -
turbopack-25..lawv.js gzip N/A 3.8 kB -
turbopack-3c..e361.js gzip N/A 3.8 kB -
turbopack-3d..vk2h.js gzip N/A 3.8 kB -
turbopack-3g..-6fl.js gzip N/A 3.8 kB -
turbopack-44..r27p.js gzip N/A 3.8 kB -
Total 448 kB 448 kB ⚠️ +43 B

Server

Middleware
Canary PR Change
middleware-b..fest.js gzip 781 B 775 B
Total 781 B 775 B ✅ -6 B
Build Details
Build Manifests
Canary PR Change
_buildManifest.js gzip 430 B 436 B 🔴 +6 B (+1%)
Total 430 B 436 B ⚠️ +6 B

📦 Webpack

Client

Main Bundles
Canary PR Change
2486.HASH.js gzip 169 B N/A -
3146-HASH.js gzip 64.2 kB N/A -
39fcf99b-HASH.js gzip 62.9 kB N/A -
8443-HASH.js gzip 4.68 kB N/A -
9431-HASH.js gzip 5.62 kB N/A -
framework-HASH.js gzip 59.8 kB 59.8 kB
main-app-HASH.js gzip 257 B 253 B 🟢 4 B (-2%)
main-HASH.js gzip 39.7 kB 40.1 kB 🔴 +452 B (+1%)
webpack-HASH.js gzip 1.68 kB 1.68 kB
6105-HASH.js gzip N/A 5.63 kB -
764.HASH.js gzip N/A 169 B -
8898-HASH.js gzip N/A 63.6 kB -
9597-HASH.js gzip N/A 4.65 kB -
e1ccab69-HASH.js gzip N/A 62.9 kB -
Total 239 kB 239 kB ✅ -212 B
Polyfills
Canary PR Change
polyfills-HASH.js gzip 39.4 kB 39.4 kB
Total 39.4 kB 39.4 kB
Pages
Canary PR Change
_app-HASH.js gzip 194 B 194 B
_error-HASH.js gzip 183 B 182 B
css-HASH.js gzip 335 B 335 B
dynamic-HASH.js gzip 1.8 kB 1.8 kB
edge-ssr-HASH.js gzip 255 B 254 B
head-HASH.js gzip 351 B 349 B
hooks-HASH.js gzip 384 B 384 B
image-HASH.js gzip 580 B 580 B
index-HASH.js gzip 259 B 259 B
link-HASH.js gzip 2.49 kB 2.49 kB
routerDirect..HASH.js gzip 319 B 319 B
script-HASH.js gzip 386 B 386 B
withRouter-HASH.js gzip 315 B 313 B
1afbb74e6ecf..834.css gzip 106 B 106 B
Total 7.96 kB 7.95 kB ✅ -7 B

Server

Edge SSR
Canary PR Change
edge-ssr.js gzip 128 kB 128 kB
page.js gzip 283 kB 285 kB 🔴 +2.13 kB (+1%)
Total 411 kB 413 kB ⚠️ +1.77 kB
Middleware
Canary PR Change
middleware-b..fest.js gzip 618 B 615 B
middleware-r..fest.js gzip 156 B 155 B
middleware.js gzip 45.3 kB 45.3 kB
edge-runtime..pack.js gzip 842 B 842 B
Total 46.9 kB 46.9 kB ⚠️ +42 B
Build Details
Build Manifests
Canary PR Change
_buildManifest.js gzip 719 B 720 B
Total 719 B 720 B ⚠️ +1 B
Build Cache
Canary PR Change
0.pack gzip 4.81 MB 4.81 MB 🟢 6.07 kB (0%)
index.pack gzip 118 kB 118 kB
index.pack.old gzip 119 kB 119 kB
Total 5.05 MB 5.05 MB ✅ -5.51 kB

🔄 Shared (bundler-independent)

Runtimes
Canary PR Change
app-page-exp...dev.js gzip 364 kB 364 kB
app-page-exp..prod.js gzip 201 kB 201 kB
app-page-tur...dev.js gzip 364 kB 364 kB
app-page-tur..prod.js gzip 201 kB 201 kB
app-page-tur...dev.js gzip 360 kB 360 kB
app-page-tur..prod.js gzip 199 kB 199 kB
app-page.run...dev.js gzip 361 kB 361 kB
app-page.run..prod.js gzip 199 kB 199 kB
app-route-ex...dev.js gzip 81.5 kB 81.5 kB
app-route-ex..prod.js gzip 55.4 kB 55.4 kB
app-route-tu...dev.js gzip 81.5 kB 81.5 kB
app-route-tu..prod.js gzip 55.4 kB 55.4 kB
app-route-tu...dev.js gzip 81.1 kB 81.1 kB
app-route-tu..prod.js gzip 55.1 kB 55.1 kB
app-route.ru...dev.js gzip 81.1 kB 81.1 kB
app-route.ru..prod.js gzip 55.1 kB 55.1 kB
dist_client_...dev.js gzip 324 B 324 B
dist_client_...dev.js gzip 326 B 326 B
dist_client_...dev.js gzip 318 B 318 B
dist_client_...dev.js gzip 317 B 317 B
pages-api-tu...dev.js gzip 45.2 kB 45.2 kB
pages-api-tu..prod.js gzip 33.9 kB 33.9 kB
pages-api.ru...dev.js gzip 45.2 kB 45.2 kB
pages-api.ru..prod.js gzip 33.9 kB 33.9 kB
pages-turbo....dev.js gzip 54.6 kB 54.6 kB
pages-turbo...prod.js gzip 39.6 kB 39.6 kB
pages.runtim...dev.js gzip 54.6 kB 54.6 kB
pages.runtim..prod.js gzip 39.5 kB 39.5 kB
server.runti..prod.js gzip 67.5 kB 67.5 kB
use-cache-pr...dev.js gzip 71.4 kB 71.4 kB
use-cache-pr...dev.js gzip 71.4 kB 71.4 kB
use-cache-pr...dev.js gzip 69.7 kB 69.7 kB
use-cache-pr...dev.js gzip 69.7 kB 69.7 kB
Total 3.49 MB 3.49 MB
📎 Tarball URL
https://vercel-packages.vercel.app/next/commits/6a7cc712f1c68bb36880364cc89b5c60db777c87/next

Commit: 6a7cc71

@github-actions

github-actions Bot commented Jul 19, 2026

Copy link
Copy Markdown
Contributor

Failing test suites

Commit: 6a7cc71 | About building and testing Next.js

pnpm test-dev-experimental-turbo test/development/app-dir/hmr-deleted-page/hmr-deleted-page.test.ts (job)

  • hmr-deleted-page > should not show errors for a deleted page (DD)
Expand output

● hmr-deleted-page › should not show errors for a deleted page

Expected no visible Redbox but found one
header: Runtime Error
./app/page/page.tsx:1:1
Error: Module not found: Can't resolve './style.css'
> 1 | import './style.css'
    | ^^^^^^^^^^^^^^^^^^^^
  2 |
  3 | import { Test } from './test'
  4 |



https://nextjs.org/docs/messages/module-not-found
Show More
description: ./app/page/page.tsx:1:1
Error: Module not found: Can't resolve './style.css'
> 1 | import './style.css'
    | ^^^^^^^^^^^^^^^^^^^^
  2 |
  3 | import { Test } from './test'
  4 |



https://nextjs.org/docs/messages/module-not-found
source: null

  25 |     await waitForHydration(browser)
  26 |
> 27 |     await waitForNoRedbox(browser)
     |     ^
  28 |     expect(await browser.elementByCss('h1').text()).toBe('404')
  29 |   })
  30 | })

  at Object.<anonymous> (development/app-dir/hmr-deleted-page/hmr-deleted-page.test.ts:27:5)

pnpm test-dev-turbo test/e2e/app-dir/navigation/navigation.test.ts (turbopack) (job)

  • app dir - navigation > hash-with-scroll-offset > should scroll to the specified hash (DD)
Expand output

● app dir - navigation › hash-with-scroll-offset › should scroll to the specified hash

expect(received).resolves.toEqual(expected) // deep equality

Expected: 108
Received: 0

  230 |         await browser.elementByCss(`#link-to-${val.toString()}`).click()
  231 |         await retry(() =>
> 232 |           expect(browser.eval('window.pageYOffset')).resolves.toEqual(
      |                                                               ^
  233 |             expectedScroll
  234 |           )
  235 |         )

  at Object.toEqual (../node_modules/.pnpm/expect@29.7.0/node_modules/expect/build/index.js:174:22)
  at toEqual (e2e/app-dir/navigation/navigation.test.ts:232:63)
  at fn (lib/next-test-utils.ts:867:20)
  at checkLink (e2e/app-dir/navigation/navigation.test.ts:231:9)
  at Object.<anonymous> (e2e/app-dir/navigation/navigation.test.ts:238:7)

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