From fd38d5bf1520b58f879eb4afeba1cb8efb428f05 Mon Sep 17 00:00:00 2001
From: Zack Tanner <1939140+ztanner@users.noreply.github.com>
Date: Sat, 21 Feb 2026 10:29:09 -0800
Subject: [PATCH] segment cache: add test for passing unawaited promise to
context provider (#89339)
Adds a failing test proving that if the RSC response from a prefetch
contains an unresolved promise, navigating to the page will cause the
Suspense boundary wrapping the use of that promise to remain in fallback
state indefinitely.
This appears to have regressed in
e9a03ac12cfe3c3fb6b16db42ade06e7382b9018, though the theory is that this
is just uncovering a React bug rather than causing the issue.
Closes #89170
---
.../prefetch-partial-rsc/app/layout.tsx | 9 ++++++
.../prefetch-partial-rsc/app/learn/layout.tsx | 9 ++++++
.../prefetch-partial-rsc/app/learn/page.tsx | 12 ++++++++
.../prefetch-partial-rsc/app/lib/get-user.ts | 12 ++++++++
.../prefetch-partial-rsc/app/page.tsx | 12 ++++++++
.../app/user-card-shell.tsx | 12 ++++++++
.../prefetch-partial-rsc/app/user-card.tsx | 9 ++++++
.../app/user-provider.tsx | 29 +++++++++++++++++++
.../prefetch-partial-rsc/next.config.js | 8 +++++
.../prefetch-partial-rsc.test.ts | 17 +++++++++++
10 files changed, 129 insertions(+)
create mode 100644 test/e2e/app-dir/segment-cache/prefetch-partial-rsc/app/layout.tsx
create mode 100644 test/e2e/app-dir/segment-cache/prefetch-partial-rsc/app/learn/layout.tsx
create mode 100644 test/e2e/app-dir/segment-cache/prefetch-partial-rsc/app/learn/page.tsx
create mode 100644 test/e2e/app-dir/segment-cache/prefetch-partial-rsc/app/lib/get-user.ts
create mode 100644 test/e2e/app-dir/segment-cache/prefetch-partial-rsc/app/page.tsx
create mode 100644 test/e2e/app-dir/segment-cache/prefetch-partial-rsc/app/user-card-shell.tsx
create mode 100644 test/e2e/app-dir/segment-cache/prefetch-partial-rsc/app/user-card.tsx
create mode 100644 test/e2e/app-dir/segment-cache/prefetch-partial-rsc/app/user-provider.tsx
create mode 100644 test/e2e/app-dir/segment-cache/prefetch-partial-rsc/next.config.js
create mode 100644 test/e2e/app-dir/segment-cache/prefetch-partial-rsc/prefetch-partial-rsc.test.ts
diff --git a/test/e2e/app-dir/segment-cache/prefetch-partial-rsc/app/layout.tsx b/test/e2e/app-dir/segment-cache/prefetch-partial-rsc/app/layout.tsx
new file mode 100644
index 0000000000000..716a8db36f52c
--- /dev/null
+++ b/test/e2e/app-dir/segment-cache/prefetch-partial-rsc/app/layout.tsx
@@ -0,0 +1,9 @@
+import { ReactNode } from 'react'
+
+export default function Root({ children }: { children: ReactNode }) {
+ return (
+
+
{children}
+
+ )
+}
diff --git a/test/e2e/app-dir/segment-cache/prefetch-partial-rsc/app/learn/layout.tsx b/test/e2e/app-dir/segment-cache/prefetch-partial-rsc/app/learn/layout.tsx
new file mode 100644
index 0000000000000..5c70dee3e39ac
--- /dev/null
+++ b/test/e2e/app-dir/segment-cache/prefetch-partial-rsc/app/learn/layout.tsx
@@ -0,0 +1,9 @@
+import { ReactNode } from 'react'
+import { getUser } from '../lib/get-user'
+import { UserProvider } from '../user-provider'
+
+export default function LearnLayout({ children }: { children: ReactNode }) {
+ const userPromise = getUser()
+
+ return {children}
+}
diff --git a/test/e2e/app-dir/segment-cache/prefetch-partial-rsc/app/learn/page.tsx b/test/e2e/app-dir/segment-cache/prefetch-partial-rsc/app/learn/page.tsx
new file mode 100644
index 0000000000000..87fc4ddc71430
--- /dev/null
+++ b/test/e2e/app-dir/segment-cache/prefetch-partial-rsc/app/learn/page.tsx
@@ -0,0 +1,12 @@
+import { UserCardShell } from '../user-card-shell'
+
+export default async function LearnPage() {
+ 'use cache'
+
+ return (
+
+
Learn
+
+
+ )
+}
diff --git a/test/e2e/app-dir/segment-cache/prefetch-partial-rsc/app/lib/get-user.ts b/test/e2e/app-dir/segment-cache/prefetch-partial-rsc/app/lib/get-user.ts
new file mode 100644
index 0000000000000..2dfb4d090a371
--- /dev/null
+++ b/test/e2e/app-dir/segment-cache/prefetch-partial-rsc/app/lib/get-user.ts
@@ -0,0 +1,12 @@
+import { cookies } from 'next/headers'
+
+export async function getUser(): Promise<{ name: string }> {
+ const cookieStore = await cookies()
+ const userCookie = cookieStore.get('user')
+
+ if (!userCookie) {
+ return { name: 'Guest' }
+ }
+
+ return { name: userCookie.value }
+}
diff --git a/test/e2e/app-dir/segment-cache/prefetch-partial-rsc/app/page.tsx b/test/e2e/app-dir/segment-cache/prefetch-partial-rsc/app/page.tsx
new file mode 100644
index 0000000000000..166b2d085c666
--- /dev/null
+++ b/test/e2e/app-dir/segment-cache/prefetch-partial-rsc/app/page.tsx
@@ -0,0 +1,12 @@
+import Link from 'next/link'
+
+export default function Page() {
+ return (
+
+
User promise demo
+
+ Go to learn
+
+
+ )
+}
diff --git a/test/e2e/app-dir/segment-cache/prefetch-partial-rsc/app/user-card-shell.tsx b/test/e2e/app-dir/segment-cache/prefetch-partial-rsc/app/user-card-shell.tsx
new file mode 100644
index 0000000000000..68b2ae89fdc2f
--- /dev/null
+++ b/test/e2e/app-dir/segment-cache/prefetch-partial-rsc/app/user-card-shell.tsx
@@ -0,0 +1,12 @@
+'use client'
+
+import { Suspense } from 'react'
+import { UserCard } from './user-card'
+
+export function UserCardShell() {
+ return (
+ loading}>
+
+
+ )
+}
diff --git a/test/e2e/app-dir/segment-cache/prefetch-partial-rsc/app/user-card.tsx b/test/e2e/app-dir/segment-cache/prefetch-partial-rsc/app/user-card.tsx
new file mode 100644
index 0000000000000..e88bdd59ad4e5
--- /dev/null
+++ b/test/e2e/app-dir/segment-cache/prefetch-partial-rsc/app/user-card.tsx
@@ -0,0 +1,9 @@
+'use client'
+
+import { useUser } from './user-provider'
+
+export function UserCard() {
+ const user = useUser()
+
+ return
user: {user?.name ?? 'unknown'}
+}
diff --git a/test/e2e/app-dir/segment-cache/prefetch-partial-rsc/app/user-provider.tsx b/test/e2e/app-dir/segment-cache/prefetch-partial-rsc/app/user-provider.tsx
new file mode 100644
index 0000000000000..35c06dd843666
--- /dev/null
+++ b/test/e2e/app-dir/segment-cache/prefetch-partial-rsc/app/user-provider.tsx
@@ -0,0 +1,29 @@
+'use client'
+
+import { createContext, ReactNode, use } from 'react'
+
+type User = { name: string } | undefined
+
+const UserContext = createContext | null>(null)
+UserContext.displayName = 'UserContext'
+
+export function UserProvider({
+ children,
+ userPromise,
+}: {
+ children: ReactNode
+ userPromise: Promise
+}) {
+ return (
+ {children}
+ )
+}
+
+export function useUser(): User {
+ const userPromise = use(UserContext)
+ if (!userPromise) {
+ throw new Error('useUser must be used within a UserProvider')
+ }
+
+ return use(userPromise)
+}
diff --git a/test/e2e/app-dir/segment-cache/prefetch-partial-rsc/next.config.js b/test/e2e/app-dir/segment-cache/prefetch-partial-rsc/next.config.js
new file mode 100644
index 0000000000000..e64bae22d6580
--- /dev/null
+++ b/test/e2e/app-dir/segment-cache/prefetch-partial-rsc/next.config.js
@@ -0,0 +1,8 @@
+/**
+ * @type {import('next').NextConfig}
+ */
+const nextConfig = {
+ cacheComponents: true,
+}
+
+module.exports = nextConfig
diff --git a/test/e2e/app-dir/segment-cache/prefetch-partial-rsc/prefetch-partial-rsc.test.ts b/test/e2e/app-dir/segment-cache/prefetch-partial-rsc/prefetch-partial-rsc.test.ts
new file mode 100644
index 0000000000000..70dfe8ac8c903
--- /dev/null
+++ b/test/e2e/app-dir/segment-cache/prefetch-partial-rsc/prefetch-partial-rsc.test.ts
@@ -0,0 +1,17 @@
+import { nextTestSetup } from 'e2e-utils'
+import { retry } from 'next-test-utils'
+
+describe('prefetch-partial-rsc', () => {
+ const { next } = nextTestSetup({
+ files: __dirname,
+ })
+
+ it('resolves after a client-side navigation', async () => {
+ const browser = await next.browser('/')
+ await browser.elementByCss('#learn-link').click()
+ await retry(async () => {
+ const text = await browser.elementByCss('#user').text()
+ expect(text).toBe('user: Guest')
+ })
+ })
+})