Code Diff
diff --git a/packages/next/src/server/lib/router-utils/resolve-routes.ts b/packages/next/src/server/lib/router-utils/resolve-routes.ts
index 62713fb587086..c8c793d45d3b1 100644
--- a/packages/next/src/server/lib/router-utils/resolve-routes.ts
+++ b/packages/next/src/server/lib/router-utils/resolve-routes.ts
@@ -474,6 +474,27 @@ export function getResolveRoutes(
if (output.locale) {
addRequestMeta(req, 'locale', output.locale)
}
+
+ if (
+ process.env.__NEXT_TEST_MODE &&
+ process.env.IS_TURBOPACK_TEST &&
+ output.type === 'nextStaticFolder' &&
+ config.deploymentId
+ ) {
+ const expectedToken = config.deploymentId
+ if (parsedUrl.query.dpl !== expectedToken) {
+ console.error(
+ `Invalid dpl query param: ${req.url}, expected: ${expectedToken}`
+ )
+ return {
+ finished: true,
+ parsedUrl,
+ resHeaders,
+ matchedOutput: null,
+ }
+ }
+ }
+
return {
parsedUrl,
resHeaders,
diff --git a/packages/next/src/server/next-server.ts b/packages/next/src/server/next-server.ts
index 65825723b735e..2479adf388749 100644
--- a/packages/next/src/server/next-server.ts
+++ b/packages/next/src/server/next-server.ts
@@ -700,9 +700,26 @@ export default class NextNodeServer extends BaseServer<
return
}
- const { isAbsolute, href } = paramsResult
+ let { href } = paramsResult
- const imageUpstream = isAbsolute
+ if (
+ process.env.__NEXT_TEST_MODE &&
+ process.env.IS_TURBOPACK_TEST &&
+ !paramsResult.isAbsolute
+ ) {
+ // Forward the dpl query param from the original /_next/image request to the
+ // internal static file request so that the static file validation in
+ // resolve-routes.ts can verify it.
+ const dpl =
+ typeof req.url === 'string'
+ ? new URL(req.url, 'http://n').searchParams.get('dpl')
+ : undefined
+ if (dpl) {
+ href += `${href.includes('?') ? '&' : '?'}dpl=${dpl}`
+ }
+ }
+
+ const imageUpstream = paramsResult.isAbsolute
? await fetchExternalImage(
href,
this.nextConfig.images.dangerouslyAllowLocalIP,
diff --git a/test/e2e/app-dir-export/test/utils.ts b/test/e2e/app-dir-export/test/utils.ts
index dce6f50178080..30494b1335f5d 100644
--- a/test/e2e/app-dir-export/test/utils.ts
+++ b/test/e2e/app-dir-export/test/utils.ts
@@ -210,6 +210,7 @@ export function runTests({
files: join(__dirname, '..'),
skipDeployment: true,
skipStart: true,
+ disableAutoSkewProtection: true,
})
if (skipped) {
return
diff --git a/test/e2e/app-dir/app-esm-js/standalone.test.ts b/test/e2e/app-dir/app-esm-js/standalone.test.ts
index ee46e10683129..cf06510ac86d6 100644
--- a/test/e2e/app-dir/app-esm-js/standalone.test.ts
+++ b/test/e2e/app-dir/app-esm-js/standalone.test.ts
@@ -52,6 +52,7 @@ if (!(globalThis as any).isNextStart) {
/- Local:/,
{
...process.env,
+ ...next.env,
PORT: appPort.toString(),
},
undefined,
diff --git a/test/e2e/app-dir/app/standalone-gsp.test.ts b/test/e2e/app-dir/app/standalone-gsp.test.ts
index be82a5f7136b7..ce7f8254489e6 100644
--- a/test/e2e/app-dir/app/standalone-gsp.test.ts
+++ b/test/e2e/app-dir/app/standalone-gsp.test.ts
@@ -69,6 +69,7 @@ if (!(globalThis as any).isNextStart) {
/- Local:/,
{
...process.env,
+ ...next.env,
PORT: appPort.toString(),
},
undefined,
diff --git a/test/e2e/app-dir/app/standalone.test.ts b/test/e2e/app-dir/app/standalone.test.ts
index 7330c74e5edc6..3769ff2c36a45 100644
--- a/test/e2e/app-dir/app/standalone.test.ts
+++ b/test/e2e/app-dir/app/standalone.test.ts
@@ -73,6 +73,7 @@ if (!(globalThis as any).isNextStart) {
/- Local:/,
{
...process.env,
+ ...next.env,
PORT: appPort.toString(),
},
undefined,
diff --git a/test/e2e/app-dir/mdx/mdx.test.ts b/test/e2e/app-dir/mdx/mdx.test.ts
index 19ab369aea2db..e4fa6a12d1d8c 100644
--- a/test/e2e/app-dir/mdx/mdx.test.ts
+++ b/test/e2e/app-dir/mdx/mdx.test.ts
@@ -61,7 +61,7 @@ for (const type of ['with-mdx-rs', 'without-mdx-rs']) {
it('should work with next/image', async () => {
const $ = await next.render$('/image')
expect($('img').attr('src')).toBe(
- '/_next/image?url=%2Ftest.jpg&w=384&q=75'
+ `/_next/image?url=%2Ftest.jpg&w=384&q=75${next.getDeploymentIdQuery(true)}`
)
})
diff --git a/test/e2e/app-dir/next-image/next-image-proxy.test.ts b/test/e2e/app-dir/next-image/next-image-proxy.test.ts
index fdbe842f10d20..48eb689c4835d 100644
--- a/test/e2e/app-dir/next-image/next-image-proxy.test.ts
+++ b/test/e2e/app-dir/next-image/next-image-proxy.test.ts
@@ -1,5 +1,5 @@
import { join } from 'path'
-import { findPort, check } from 'next-test-utils'
+import { findPort, retry } from 'next-test-utils'
import https from 'https'
import httpProxy from 'http-proxy'
import fs from 'fs'
@@ -77,32 +77,21 @@ describe('next-image-proxy', () => {
})
const local = await browser.elementByCss('#app-page').getAttribute('src')
-
- if (process.env.IS_TURBOPACK_TEST) {
- expect(local).toMatchInlineSnapshot(
- `"/_next/image?url=%2F_next%2Fstatic%2Fmedia%2Ftest.c5ae911e.png&w=828&q=90"`
- )
- } else {
- expect(local).toMatchInlineSnapshot(
- `"/_next/image?url=%2F_next%2Fstatic%2Fmedia%2Ftest.3f1a293b.png&w=828&q=90"`
- )
- }
+ expect(local.replace(/test\.[0-9a-f]{8,}\.png/g, 'test.HASH.png')).toEqual(
+ `/_next/image?url=%2F_next%2Fstatic%2Fmedia%2Ftest.HASH.png&w=828&q=90${next.getAssetQuery(true)}`
+ )
const remote = await browser
.elementByCss('#remote-app-page')
.getAttribute('src')
- if (process.env.IS_TURBOPACK_TEST) {
- expect(remote).toMatchInlineSnapshot(
- `"/_next/image?url=https%3A%2F%2Fimage-optimization-test.vercel.app%2Ftest.jpg&w=640&q=90"`
- )
- } else {
- expect(remote).toMatchInlineSnapshot(
- `"/_next/image?url=https%3A%2F%2Fimage-optimization-test.vercel.app%2Ftest.jpg&w=640&q=90"`
- )
- }
+ expect(remote).toEqual(
+ `/_next/image?url=https%3A%2F%2Fimage-optimization-test.vercel.app%2Ftest.jpg&w=640&q=90`
+ )
- const expected = JSON.stringify({ fulfilledCount: 4, failCount: 0 })
- await check(() => JSON.stringify({ fulfilledCount, failCount }), expected)
+ await retry(() => {
+ expect(fulfilledCount).toBe(4)
+ expect(failCount).toBe(0)
+ })
await browser.close()
})
diff --git a/test/e2e/app-dir/next-image/next-image.test.ts b/test/e2e/app-dir/next-image/next-image.test.ts
index 2a01785be7acc..217b5cda7bb4a 100644
--- a/test/e2e/app-dir/next-image/next-image.test.ts
+++ b/test/e2e/app-dir/next-image/next-image.test.ts
@@ -1,151 +1,106 @@
-import { nextTestSetup } from 'e2e-utils'
+import { isNextDeploy, nextTestSetup } from 'e2e-utils'
import fs from 'fs-extra'
import { join } from 'path'
describe('app dir - next-image', () => {
- const { next, skipped } = nextTestSetup({
+ const { next } = nextTestSetup({
files: __dirname,
- skipDeployment: true,
})
- if (skipped) {
- return
- }
-
describe('ssr content', () => {
- it('should handle HEAD requests for uncached images', async () => {
- const imagesDir = join(next.testDir, '.next/cache/images')
- await fs.remove(imagesDir).catch(() => {})
-
- const $ = await next.render$('/')
- const imageUrl = $('#app-layout').attr('src')
-
- const headRes = await next.fetch(imageUrl, { method: 'HEAD' })
- expect(headRes.status).toBe(200)
- expect(headRes.headers.get('content-type')).toMatch(/^image\//)
- expect(headRes.headers.get('X-Nextjs-Cache')).toBe('MISS')
-
- const contentLength = headRes.headers.get('content-length')
- expect(Number(contentLength || '0')).toBeGreaterThan(0)
- const headBody = await headRes.arrayBuffer()
- expect(headBody.byteLength).toBe(0)
-
- const getRes = await next.fetch(imageUrl)
- expect(getRes.status).toBe(200)
- expect(getRes.headers.get('content-type')).toMatch(/^image\//)
- expect(getRes.headers.get('X-Nextjs-Cache')).toBe('HIT')
-
- const getContentLength = getRes.headers.get('content-length')
- expect(Number(getContentLength || '0')).toBeGreaterThan(0)
-
- const getBody = await getRes.arrayBuffer()
- expect(getBody.byteLength).toBeGreaterThan(0)
- expect(getBody.byteLength).toBe(Number(getContentLength))
- })
+ if (!isNextDeploy) {
+ it('should handle HEAD requests for uncached images', async () => {
+ const imagesDir = join(next.testDir, '.next/cache/images')
+ await fs.remove(imagesDir).catch(() => {})
+
+ const $ = await next.render$('/')
+ const imageUrl = $('#app-layout').attr('src')
+
+ const headRes = await next.fetch(imageUrl, { method: 'HEAD' })
+ expect(headRes.status).toBe(200)
+ expect(headRes.headers.get('content-type')).toMatch(/^image\//)
+ expect(headRes.headers.get('X-Nextjs-Cache')).toBe('MISS')
+
+ const contentLength = headRes.headers.get('content-length')
+ expect(Number(contentLength || '0')).toBeGreaterThan(0)
+ const headBody = await headRes.arrayBuffer()
+ expect(headBody.byteLength).toBe(0)
+
+ const getRes = await next.fetch(imageUrl)
+ expect(getRes.status).toBe(200)
+ expect(getRes.headers.get('content-type')).toMatch(/^image\//)
+ expect(getRes.headers.get('X-Nextjs-Cache')).toBe('HIT')
+
+ const getContentLength = getRes.headers.get('content-length')
+ expect(Number(getContentLength || '0')).toBeGreaterThan(0)
+
+ const getBody = await getRes.arrayBuffer()
+ expect(getBody.byteLength).toBeGreaterThan(0)
+ expect(getBody.byteLength).toBe(Number(getContentLength))
+ })
+ }
it('should render images on / route', async () => {
const $ = await next.render$('/')
const layout = $('#app-layout')
-
- if (process.env.IS_TURBOPACK_TEST) {
- expect(layout.attr('src')).toMatchInlineSnapshot(
- `"/_next/image?url=%2F_next%2Fstatic%2Fmedia%2Ftest.c5ae911e.png&w=828&q=85"`
- )
- } else {
- expect(layout.attr('src')).toMatchInlineSnapshot(
- `"/_next/image?url=%2F_next%2Fstatic%2Fmedia%2Ftest.3f1a293b.png&w=828&q=85"`
- )
- }
-
- if (process.env.IS_TURBOPACK_TEST) {
- expect(layout.attr('srcset')).toMatchInlineSnapshot(
- `"/_next/image?url=%2F_next%2Fstatic%2Fmedia%2Ftest.c5ae911e.png&w=640&q=85 1x, /_next/image?url=%2F_next%2Fstatic%2Fmedia%2Ftest.c5ae911e.png&w=828&q=85 2x"`
- )
- } else {
- expect(layout.attr('srcset')).toMatchInlineSnapshot(
- `"/_next/image?url=%2F_next%2Fstatic%2Fmedia%2Ftest.3f1a293b.png&w=640&q=85 1x, /_next/image?url=%2F_next%2Fstatic%2Fmedia%2Ftest.3f1a293b.png&w=828&q=85 2x"`
- )
- }
+ expect(stripTestHash(layout.attr('src'))).toBe(
+ `/_next/image?url=%2F_next%2Fstatic%2Fmedia%2Ftest.HASH.png&w=828&q=85${next.getAssetQuery(true)}`
+ )
+ expect(stripTestHash(layout.attr('srcset'))).toBe(
+ `/_next/image?url=%2F_next%2Fstatic%2Fmedia%2Ftest.HASH.png&w=640&q=85${next.getAssetQuery(true)} 1x, /_next/image?url=%2F_next%2Fstatic%2Fmedia%2Ftest.HASH.png&w=828&q=85${next.getAssetQuery(true)} 2x`
+ )
const page = $('#app-page')
-
- if (process.env.IS_TURBOPACK_TEST) {
- expect(page.attr('src')).toMatchInlineSnapshot(
- `"/_next/image?url=%2F_next%2Fstatic%2Fmedia%2Ftest.c5ae911e.png&w=828&q=90"`
- )
- } else {
- expect(page.attr('src')).toMatchInlineSnapshot(
- `"/_next/image?url=%2F_next%2Fstatic%2Fmedia%2Ftest.3f1a293b.png&w=828&q=90"`
- )
- }
-
- if (process.env.IS_TURBOPACK_TEST) {
- expect(page.attr('srcset')).toMatchInlineSnapshot(
- `"/_next/image?url=%2F_next%2Fstatic%2Fmedia%2Ftest.c5ae911e.png&w=640&q=90 1x, /_next/image?url=%2F_next%2Fstatic%2Fmedia%2Ftest.c5ae911e.png&w=828&q=90 2x"`
- )
- } else {
- expect(page.attr('srcset')).toMatchInlineSnapshot(
- `"/_next/image?url=%2F_next%2Fstatic%2Fmedia%2Ftest.3f1a293b.png&w=640&q=90 1x, /_next/image?url=%2F_next%2Fstatic%2Fmedia%2Ftest.3f1a293b.png&w=828&q=90 2x"`
- )
- }
+ expect(stripTestHash(page.attr('src'))).toBe(
+ `/_next/image?url=%2F_next%2Fstatic%2Fmedia%2Ftest.HASH.png&w=828&q=90${next.getAssetQuery(true)}`
+ )
+ expect(stripTestHash(page.attr('srcset'))).toBe(
+ `/_next/image?url=%2F_next%2Fstatic%2Fmedia%2Ftest.HASH.png&w=640&q=90${next.getAssetQuery(true)} 1x, /_next/image?url=%2F_next%2Fstatic%2Fmedia%2Ftest.HASH.png&w=828&q=90${next.getAssetQuery(true)} 2x`
+ )
const comp = $('#app-comp')
-
- if (process.env.IS_TURBOPACK_TEST) {
- expect(comp.attr('src')).toMatchInlineSnapshot(
- `"/_next/image?url=%2F_next%2Fstatic%2Fmedia%2Ftest.c5ae911e.png&w=828&q=80"`
- )
- } else {
- expect(comp.attr('src')).toMatchInlineSnapshot(
- `"/_next/image?url=%2F_next%2Fstatic%2Fmedia%2Ftest.3f1a293b.png&w=828&q=80"`
- )
- }
-
- if (process.env.IS_TURBOPACK_TEST) {
- expect(comp.attr('srcset')).toMatchInlineSnapshot(
- `"/_next/image?url=%2F_next%2Fstatic%2Fmedia%2Ftest.c5ae911e.png&w=640&q=80 1x, /_next/image?url=%2F_next%2Fstatic%2Fmedia%2Ftest.c5ae911e.png&w=828&q=80 2x"`
- )
- } else {
- expect(comp.attr('srcset')).toMatchInlineSnapshot(
- `"/_next/image?url=%2F_next%2Fstatic%2Fmedia%2Ftest.3f1a293b.png&w=640&q=80 1x, /_next/image?url=%2F_next%2Fstatic%2Fmedia%2Ftest.3f1a293b.png&w=828&q=80 2x"`
- )
- }
+ expect(stripTestHash(comp.attr('src'))).toBe(
+ `/_next/image?url=%2F_next%2Fstatic%2Fmedia%2Ftest.HASH.png&w=828&q=80${next.getAssetQuery(true)}`
+ )
+ expect(stripTestHash(comp.attr('srcset'))).toBe(
+ `/_next/image?url=%2F_next%2Fstatic%2Fmedia%2Ftest.HASH.png&w=640&q=80${next.getAssetQuery(true)} 1x, /_next/image?url=%2F_next%2Fstatic%2Fmedia%2Ftest.HASH.png&w=828&q=80${next.getAssetQuery(true)} 2x`
+ )
})
it('should render images on /client route', async () => {
const $ = await next.render$('/client')
const root = $('#app-layout')
- expect(root.attr('src')).toMatch(
-
... [truncated]