dari-ai/nextjs-selfbench
Next.js Selfbench Next.js Selfbench is a 47-task software-engineering evaluation packaged for the Harbor evaluation framework. Each task asks an agent to implement a change in a frozen revision of vercel/next.js, then evaluates the resulting patch with task-specific tests. This repository contains the raw evaluation only. It does not include model outputs, scores, costs, or benchmark result artifacts. Use Download the raw task package with the Hugging Face CLI: hf… See the full description on the dataset page: https://huggingface.co/datasets/dari-ai/nextjs-selfbench.
0448
1diff --git a/test/e2e/app-dir/use-cache-after-uncached-io/app/layout.tsx b/test/e2e/app-dir/use-cache-after-uncached-io/app/layout.tsx2new file mode 1006443index 00000000..716a8db34--- /dev/null5+++ b/test/e2e/app-dir/use-cache-after-uncached-io/app/layout.tsx6@@ -0,0 +1,9 @@7+import { ReactNode } from 'react'8+9+export default function Root({ children }: { children: ReactNode }) {10+ return (11+ <html>12+ <body>{children}</body>13+ </html>14+ )15+}16diff --git a/test/e2e/app-dir/use-cache-after-uncached-io/app/page.tsx b/test/e2e/app-dir/use-cache-after-uncached-io/app/page.tsx17new file mode 10064418index 00000000..4f162a8719--- /dev/null20+++ b/test/e2e/app-dir/use-cache-after-uncached-io/app/page.tsx21@@ -0,0 +1,9 @@22+import { LinkAccordion } from '../components/link-accordion'23+24+export default function Page() {25+ return (26+ <main>27+ <LinkAccordion href="/uses-cache">/uses-cache</LinkAccordion>28+ </main>29+ )30+}31diff --git a/test/e2e/app-dir/use-cache-after-uncached-io/app/revalidate-uses-cache/route.ts b/test/e2e/app-dir/use-cache-after-uncached-io/app/revalidate-uses-cache/route.ts32new file mode 10064433index 00000000..1ba4704e34--- /dev/null35+++ b/test/e2e/app-dir/use-cache-after-uncached-io/app/revalidate-uses-cache/route.ts36@@ -0,0 +1,31 @@37+import { revalidatePath, revalidateTag } from 'next/cache'38+39+type State = { attempts: number; executions: number; settlements: number }40+41+function getState(): State {42+ const testGlobal = globalThis as typeof globalThis & {43+ __nextUseCacheAfterUncachedIOState?: State44+ }45+ return (testGlobal.__nextUseCacheAfterUncachedIOState ??= {46+ attempts: 0,47+ executions: 0,48+ settlements: 0,49+ })50+}51+52+export function GET() {53+ return Response.json(getState())54+}55+56+/** Evicts the entry so the prefetch's fill actually runs. */57+export async function POST() {58+ revalidateTag('data', { expire: 0 })59+ revalidatePath('/uses-cache')60+61+ const state = getState()62+ state.attempts = 063+ state.executions = 064+ state.settlements = 065+66+ return Response.json({ ok: true })67+}68diff --git a/test/e2e/app-dir/use-cache-after-uncached-io/app/uses-cache/page.tsx b/test/e2e/app-dir/use-cache-after-uncached-io/app/uses-cache/page.tsx69new file mode 10064470index 00000000..1ea041ab71--- /dev/null72+++ b/test/e2e/app-dir/use-cache-after-uncached-io/app/uses-cache/page.tsx73@@ -0,0 +1,53 @@74+import { cacheLife, cacheTag } from 'next/cache'75+import { Suspense } from 'react'76+77+type State = { attempts: number; executions: number; settlements: number }78+79+function getState(): State {80+ const testGlobal = globalThis as typeof globalThis & {81+ __nextUseCacheAfterUncachedIOState?: State82+ }83+ return (testGlobal.__nextUseCacheAfterUncachedIOState ??= {84+ attempts: 0,85+ executions: 0,86+ settlements: 0,87+ })88+}89+90+export default function Page() {91+ return (92+ <main>93+ This page uses a cache94+ <Suspense fallback={<p id="fallback">Loading…</p>}>95+ <Late />96+ </Suspense>97+ </main>98+ )99+}100+101+async function Late() {102+ // In a prerender, this will resolve after the prerender is already aborted103+ // (both in prospective and final prerenders)104+ await new Promise((resolve) => setTimeout(resolve, 1000))105+106+ getState().attempts++107+ try {108+ const result = await getCachedData()109+ return <p id="data">{result}</p>110+ } finally {111+ getState().settlements++112+ // Retained for compatibility with older versions of this regression test.113+ console.log('after-cache-read')114+ }115+}116+117+async function getCachedData(): Promise<string> {118+ 'use cache'119+ cacheLife('hours')120+ cacheTag('data')121+122+ getState().executions++123+ console.log('running getCachedData')124+125+ return 'cached-data: ' + Date.now()126+}127diff --git a/test/e2e/app-dir/use-cache-after-uncached-io/components/link-accordion.tsx b/test/e2e/app-dir/use-cache-after-uncached-io/components/link-accordion.tsx128new file mode 100644129index 00000000..b3bf56b6130--- /dev/null131+++ b/test/e2e/app-dir/use-cache-after-uncached-io/components/link-accordion.tsx132@@ -0,0 +1,32 @@133+'use client'134+import Link from 'next/link'135+import { useState } from 'react'136+137+export function LinkAccordion({138+ href,139+ children,140+ prefetch,141+}: {142+ href: string143+ children: React.ReactNode144+ prefetch?: boolean145+}) {146+ const [isVisible, setIsVisible] = useState(false)147+ return (148+ <>149+ <input150+ type="checkbox"151+ checked={isVisible}152+ onChange={() => setIsVisible(!isVisible)}153+ data-link-accordion={href}154+ />155+ {isVisible ? (156+ <Link href={href} prefetch={prefetch}>157+ {children}158+ </Link>159+ ) : (160+ `${children} (link is hidden)`161+ )}162+ </>163+ )164+}165diff --git a/test/e2e/app-dir/use-cache-after-uncached-io/next.config.ts b/test/e2e/app-dir/use-cache-after-uncached-io/next.config.ts166new file mode 100644167index 00000000..49c8b8d7168--- /dev/null169+++ b/test/e2e/app-dir/use-cache-after-uncached-io/next.config.ts170@@ -0,0 +1,7 @@171+import { NextConfig } from 'next'172+173+const nextConfig: NextConfig = {174+ cacheComponents: true,175+}176+177+export default nextConfig178diff --git a/test/e2e/app-dir/use-cache-after-uncached-io/use-cache-after-uncached-io.test.ts b/test/e2e/app-dir/use-cache-after-uncached-io/use-cache-after-uncached-io.test.ts179new file mode 100644180index 00000000..b4e84ace181--- /dev/null182+++ b/test/e2e/app-dir/use-cache-after-uncached-io/use-cache-after-uncached-io.test.ts183@@ -0,0 +1,90 @@184+import { nextTestSetup } from 'e2e-utils'185+import * as Playwright from 'playwright'186+import { createRouterAct } from '../../../lib/router-act'187+import { retry } from '../../../lib/next-test-utils'188+189+type CacheState = {190+ attempts: number191+ executions: number192+ settlements: number193+}194+195+describe('use cache reached after uncancellable asynchronous work', () => {196+ const { next, isNextStart } = nextTestSetup({197+ files: __dirname,198+ })199+200+ if (isNextStart) {201+ it('does not start an abandoned fill or poison a later request', async () => {202+ // This is a regression test for:203+ // https://github.com/vercel/next.js/issues/96339204+ // Asynchronous work can reach a cache call after its prerender has ended.205+ // That abandoned call must not execute a fill or affect a later live request.206+207+ // Revalidate /uses-cache (and, just to be safe, the cache that it references)208+ // so that we have a fresh prerender we can assert on209+ await next.fetch('/revalidate-uses-cache', { method: 'POST' })210+211+ let page: Playwright.Page212+ const browser = await next.browser('/', {213+ beforePageLoad(p) {214+ page = p215+ },216+ })217+ const act = createRouterAct(page)218+219+ // Prefetch the page, triggering a fresh prerender220+ await act(async () => {221+ await browser222+ .elementByCss('input[data-link-accordion="/uses-cache"]')223+ .click()224+ })225+226+ // Synchronize through a fixture endpoint instead of implementation logs.227+ // The delayed component reached and settled its abandoned cache call, but228+ // the cache function itself must not have started.229+ let abandonedAttempts = 0230+ await retry(async () => {231+ const state = (await next232+ .fetch('/revalidate-uses-cache')233+ .then((response) => response.json())) as CacheState234+ expect(state.attempts).toBeGreaterThan(0)235+ expect(state.executions).toBe(0)236+ expect(state.settlements).toBe(state.attempts)237+ abandonedAttempts = state.attempts238+ })239+240+ // Navigate to the page. The response should include the cache241+ await act(242+ async () => {243+ await browser.elementByCss('a[href="/uses-cache"]').click()244+ },245+246+ { includes: 'cached-data' }247+ )248+249+ const value = await browser.elementByCss('#data').text()250+ expect(value).toMatch(/cached-data: \d+/)251+252+ // The later live render performs the first and only cache fill.253+ expect(254+ (await next255+ .fetch('/revalidate-uses-cache')256+ .then((response) => response.json())) as CacheState257+ ).toEqual({258+ attempts: abandonedAttempts + 1,259+ executions: 1,260+ settlements: abandonedAttempts + 1,261+ })262+ })263+ } else {264+ it('resolves in dev', async () => {265+ // There's no prefetching in dev, so the best we can do is266+ // test that the cache resolves as expected.267+ const browser = await next.browser('/uses-cache')268+ expect(await browser.elementByCss('#data').text()).toMatch(269+ /cached-data: \d+/270+ )271+ })272+ }273+})274 