diff --git a/tests/e2e/simple-app.test.ts b/tests/e2e/simple-app.test.ts index 15f1f585fe..84372ab295 100644 --- a/tests/e2e/simple-app.test.ts +++ b/tests/e2e/simple-app.test.ts @@ -272,3 +272,42 @@ test('can require CJS module that is not bundled', async ({ simple }) => { expect(parsedBody.notBundledCJSModule.isBundled).toEqual(false) expect(parsedBody.bundledCJSModule.isBundled).toEqual(true) }) + +test('next/after callback is executed and finishes', async ({ page, simple }) => { + test.skip(!nextVersionSatisfies('>=15.0.0'), 'This test is only for Next.js 15+') + + // trigger initial request to check page which might be stale and allow regenerating in background + await page.goto(`${simple.url}/after/check`) + + await new Promise((resolve) => setTimeout(resolve, 5000)) + + // after it was possibly regenerated we can start checking actual content of the page + await page.goto(`${simple.url}/after/check`) + const pageInfoLocator1 = await page.locator('#page-info') + const pageInfo1 = JSON.parse((await pageInfoLocator1.textContent()) ?? '{}') + + expect(typeof pageInfo1?.timestamp, 'Check page should have timestamp').toBe('number') + + await page.goto(`${simple.url}/after/check`) + const pageInfoLocator2 = await page.locator('#page-info') + const pageInfo2 = JSON.parse((await pageInfoLocator2.textContent()) ?? '{}') + + expect(typeof pageInfo2?.timestamp, 'Check page should have timestamp').toBe('number') + + expect(pageInfo2.timestamp, 'Check page should be cached').toBe(pageInfo1.timestamp) + + await page.goto(`${simple.url}/after/trigger`) + + // wait for next/after to trigger revalidation of check page + await new Promise((resolve) => setTimeout(resolve, 5000)) + + await page.goto(`${simple.url}/after/check`) + const pageInfoLocator3 = await page.locator('#page-info') + const pageInfo3 = JSON.parse((await pageInfoLocator3.textContent()) ?? '{}') + + expect(typeof pageInfo3?.timestamp, 'Check page should have timestamp').toBe('number') + expect( + pageInfo3.timestamp, + 'Check page should be invalidated with newer timestamp', + ).toBeGreaterThan(pageInfo1.timestamp) +}) diff --git a/tests/fixtures/simple/app/after/check/page.js b/tests/fixtures/simple/app/after/check/page.js new file mode 100644 index 0000000000..8139df1390 --- /dev/null +++ b/tests/fixtures/simple/app/after/check/page.js @@ -0,0 +1,13 @@ +export const revalidate = 3600 // arbitrarily long, just so that it doesn't happen during a test run + + +export default async function Page() { + const data = { + timestamp: Date.now(), + } + console.log('/timestamp/key/[key] rendered', data) + + return ( +
{JSON.stringify(data)}
+ ) +} diff --git a/tests/fixtures/simple/app/after/trigger/page.js b/tests/fixtures/simple/app/after/trigger/page.js new file mode 100644 index 0000000000..62f02d8148 --- /dev/null +++ b/tests/fixtures/simple/app/after/trigger/page.js @@ -0,0 +1,17 @@ +import { revalidatePath } from 'next/cache' +import { unstable_after as after, connection } from 'next/server' + +export default async function Page() { + await connection() + after(async () => { + // this will run after response was sent + console.log('after() triggered') + console.log('after() sleep 1s') + await new Promise((resolve) => setTimeout(resolve, 1000)) + + console.log('after() revalidatePath /after/check') + revalidatePath('/after/check') + }) + + return
Page with after()
+} diff --git a/tests/fixtures/simple/next.config.js b/tests/fixtures/simple/next.config.js index 57e41bf927..111e8fb635 100644 --- a/tests/fixtures/simple/next.config.js +++ b/tests/fixtures/simple/next.config.js @@ -8,6 +8,7 @@ const nextConfig = { outputFileTracingIncludes: { '/': ['public/**'], }, + after: true }, images: { remotePatterns: [