diff --git a/packages/next/src/server/image-optimizer.ts b/packages/next/src/server/image-optimizer.ts
index 68a5e57645cf7..0d350f422cdb3 100644
--- a/packages/next/src/server/image-optimizer.ts
+++ b/packages/next/src/server/image-optimizer.ts
@@ -138,6 +138,9 @@ export function detectContentType(buffer: Buffer) {
if ([0x3c, 0x3f, 0x78, 0x6d, 0x6c].every((b, i) => buffer[i] === b)) {
return SVG
}
+ if ([0x3c, 0x73, 0x76, 0x67].every((b, i) => buffer[i] === b)) {
+ return SVG
+ }
if (
[0, 0, 0, 0, 0x66, 0x74, 0x79, 0x70, 0x61, 0x76, 0x69, 0x66].every(
(b, i) => !b || buffer[i] === b
diff --git a/test/integration/image-optimizer/app/pages/api/application.svg.js b/test/integration/image-optimizer/app/pages/api/application.svg.js
index 1531b13aa9f6e..59cda0da6288f 100644
--- a/test/integration/image-optimizer/app/pages/api/application.svg.js
+++ b/test/integration/image-optimizer/app/pages/api/application.svg.js
@@ -1,6 +1,4 @@
export default function handler(_req, res) {
res.setHeader('Content-Type', 'application/svg+xml')
- res.end(
- ``
- )
+ res.end('body is not svg to force fallback to Content-Type header')
}
diff --git a/test/integration/image-optimizer/app/pages/api/comma.svg.js b/test/integration/image-optimizer/app/pages/api/comma.svg.js
index 5fa8a430a56d5..5eea24e39bba4 100644
--- a/test/integration/image-optimizer/app/pages/api/comma.svg.js
+++ b/test/integration/image-optimizer/app/pages/api/comma.svg.js
@@ -1,6 +1,4 @@
export default function handler(_req, res) {
res.setHeader('Content-Type', 'image/foo, text/html')
- res.end(
- ``
- )
+ res.end('body is not svg to force fallback to Content-Type header')
}
diff --git a/test/integration/image-optimizer/app/pages/api/stateful/test.png.js b/test/integration/image-optimizer/app/pages/api/stateful/test.png.js
new file mode 100644
index 0000000000000..35fa0040bccbb
--- /dev/null
+++ b/test/integration/image-optimizer/app/pages/api/stateful/test.png.js
@@ -0,0 +1,19 @@
+const images = [
+ 'iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAADUlEQVR42mP8z8DwHwAFBQIAX8jx0gAAAABJRU5ErkJggg==',
+ 'iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAADUlEQVR42mNk+M/wHwAEBgIApD5fRAAAAABJRU5ErkJggg==',
+ 'iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAADUlEQVR42mNkYPj/HwADBwIAMCbHYQAAAABJRU5ErkJggg==',
+ 'iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAADUlEQVR42mP8/5/hPwAIAgL/4d1j8wAAAABJRU5ErkJggg==',
+ 'iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAADUlEQVR42mNk+P//PwAGBAL/VJiKjgAAAABJRU5ErkJggg==',
+ 'iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAQAAAC1HAwCAAAAC0lEQVR42mNk+A8AAQUBAScY42YAAAAASUVORK5CYII=',
+ 'iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAQAAAC1HAwCAAAAC0lEQVR42mP8/x8AAwMCAO+ip1sAAAAASUVORK5CYII=',
+]
+
+let state = 0
+
+export default function handler(_req, res) {
+ state++
+ const index = state % images.length
+ const buffer = Buffer.from(images[index], 'base64')
+ res.setHeader('Content-Type', 'image/png')
+ return res.end(buffer)
+}
diff --git a/test/integration/image-optimizer/app/pages/api/uppercase.svg.js b/test/integration/image-optimizer/app/pages/api/uppercase.svg.js
index 6958255491f07..aa62434d59e11 100644
--- a/test/integration/image-optimizer/app/pages/api/uppercase.svg.js
+++ b/test/integration/image-optimizer/app/pages/api/uppercase.svg.js
@@ -1,6 +1,4 @@
export default function handler(_req, res) {
res.setHeader('Content-Type', 'image/SVG+XML')
- res.end(
- ``
- )
+ res.end('body is not svg to force fallback to Content-Type header')
}
diff --git a/test/integration/image-optimizer/app/pages/api/wrong-header.svg.js b/test/integration/image-optimizer/app/pages/api/wrong-header.svg.js
new file mode 100644
index 0000000000000..d7203ef8d632c
--- /dev/null
+++ b/test/integration/image-optimizer/app/pages/api/wrong-header.svg.js
@@ -0,0 +1,6 @@
+export default function handler(_req, res) {
+ res.setHeader('Content-Type', 'image/png')
+ res.end(
+ ``
+ )
+}
diff --git a/test/integration/image-optimizer/test/dangerously-allow-svg.test.ts b/test/integration/image-optimizer/test/dangerously-allow-svg.test.ts
new file mode 100644
index 0000000000000..986dab2ec8e74
--- /dev/null
+++ b/test/integration/image-optimizer/test/dangerously-allow-svg.test.ts
@@ -0,0 +1,13 @@
+import { join } from 'path'
+import { setupTests } from './util'
+
+const appDir = join(__dirname, '../app')
+const imagesDir = join(appDir, '.next', 'cache', 'images')
+
+describe('with dangerouslyAllowSVG config', () => {
+ setupTests({
+ nextConfigImages: { dangerouslyAllowSVG: true },
+ appDir,
+ imagesDir,
+ })
+})
diff --git a/test/integration/image-optimizer/test/util.ts b/test/integration/image-optimizer/test/util.ts
index 4a69b6f6586d9..362e015d92327 100644
--- a/test/integration/image-optimizer/test/util.ts
+++ b/test/integration/image-optimizer/test/util.ts
@@ -17,6 +17,21 @@ import {
import isAnimated from 'next/dist/compiled/is-animated'
import type { RequestInit } from 'node-fetch'
+type SetupTestsCtx = {
+ appDir: string
+ imagesDir: string
+ nextConfigImages?: Partial
+ isDev?: boolean
+}
+
+type RunTestsCtx = SetupTestsCtx & {
+ w: number
+ app?: import('child_process').ChildProcess
+ appDir?: string
+ appPort?: number
+ nextOutput?: string
+}
+
const largeSize = 1080 // defaults defined in server/config.ts
const animatedWarnText =
'is an animated image so it will not be optimized. Consider adding the "unoptimized" property to the .'
@@ -53,7 +68,7 @@ export async function serveSlowImage() {
}
}
-export async function fsToJson(dir, output = {}) {
+export async function fsToJson(dir: string, output = {}) {
const files = await fs.readdir(dir)
for (let file of files) {
const fsPath = join(dir, file)
@@ -77,12 +92,16 @@ export async function expectWidth(res, w, { expectAnimated = false } = {}) {
expect(isAnimated(buffer)).toBe(expectAnimated)
}
-export const cleanImagesDir = async (ctx) => {
+export const cleanImagesDir = async (ctx: { imagesDir: string }) => {
console.warn('Cleaning', ctx.imagesDir)
await fs.remove(ctx.imagesDir)
}
-async function expectAvifSmallerThanWebp(w, q, appPort) {
+async function expectAvifSmallerThanWebp(
+ w: number,
+ q: number,
+ appPort: number
+) {
const query = { url: '/mountains.jpg', w, q }
const res1 = await fetchViaHTTP(appPort, '/_next/image', query, {
headers: {
@@ -130,7 +149,7 @@ async function fetchWithDuration(
return { duration, buffer, res }
}
-export function runTests(ctx) {
+export function runTests(ctx: RunTestsCtx) {
const { isDev, nextConfigImages } = ctx
const {
contentDispositionType = 'inline',
@@ -279,7 +298,7 @@ export function runTests(ctx) {
expect(ctx.nextOutput).toContain(animatedWarnText)
})
- if (ctx.dangerouslyAllowSVG) {
+ if (ctx.nextConfigImages?.dangerouslyAllowSVG) {
it('should maintain vector svg', async () => {
const query = { w: ctx.w, q: 90, url: '/test.svg' }
const opts = { headers: { accept: 'image/webp' } }
@@ -303,7 +322,12 @@ export function runTests(ctx) {
'utf8'
)
expect(actual).toMatch(expected)
- expect(ctx.nextOutput).not.toContain('The requested resource')
+ expect(ctx.nextOutput).not.toContain(
+ `The requested resource isn't a valid image`
+ )
+ expect(ctx.nextOutput).not.toContain(
+ `valid but image type is not allowed`
+ )
})
} else {
it('should not allow vector svg', async () => {
@@ -343,6 +367,16 @@ export function runTests(ctx) {
'"url" parameter is valid but image type is not allowed'
)
})
+
+ it('should not allow svg with wrong header', async () => {
+ const query = { w: ctx.w, q: 65, url: '/api/wrong-header.svg' }
+ const opts = { headers: { accept: 'image/webp' } }
+ const res = await fetchViaHTTP(ctx.appPort, '/_next/image', query, opts)
+ expect(res.status).toBe(400)
+ expect(await res.text()).toContain(
+ '"url" parameter is valid but image type is not allowed'
+ )
+ })
}
it('should maintain ico format', async () => {
@@ -820,8 +854,14 @@ export function runTests(ctx) {
await cleanImagesDir(ctx)
const delay = 500
+ if (globalThis.isrImgQuality) {
+ globalThis.isrImgQuality++
+ } else {
+ globalThis.isrImgQuality = 40
+ }
+
const url = `http://localhost:${slowImageServer.port}/slow.png?delay=${delay}`
- const query = { url, w: ctx.w, q: 39 }
+ const query = { url, w: ctx.w, q: globalThis.isrImgQuality }
const opts = { headers: { accept: 'image/webp' } }
const one = await fetchWithDuration(
@@ -864,9 +904,9 @@ export function runTests(ctx) {
const json2 = await fsToJson(ctx.imagesDir)
expect(json2).toStrictEqual(json1)
- if (ctx.minimumCacheTTL) {
+ if (ctx.nextConfigImages?.minimumCacheTTL) {
// Wait until expired so we can confirm image is regenerated
- await waitFor(ctx.minimumCacheTTL * 1000)
+ await waitFor(ctx.nextConfigImages.minimumCacheTTL * 1000)
const [three, four] = await Promise.all([
fetchWithDuration(ctx.appPort, '/_next/image', query, opts),
@@ -968,7 +1008,17 @@ export function runTests(ctx) {
it('should use cache and stale-while-revalidate when query is the same for internal image', async () => {
await cleanImagesDir(ctx)
- const query = { url: '/test.png', w: ctx.w, q: 80 }
+ if (globalThis.isrImgQuality) {
+ globalThis.isrImgQuality++
+ } else {
+ globalThis.isrImgQuality = 80
+ }
+
+ const query = {
+ url: '/api/stateful/test.png',
+ w: ctx.w,
+ q: globalThis.isrImgQuality,
+ }
const opts = { headers: { accept: 'image/webp' } }
const one = await fetchWithDuration(
@@ -1010,16 +1060,15 @@ export function runTests(ctx) {
const json2 = await fsToJson(ctx.imagesDir)
expect(json2).toStrictEqual(json1)
- if (ctx.minimumCacheTTL) {
+ if (ctx.nextConfigImages?.minimumCacheTTL) {
// Wait until expired so we can confirm image is regenerated
- await waitFor(ctx.minimumCacheTTL * 1000)
+ await waitFor(ctx.nextConfigImages.minimumCacheTTL * 1000)
const [three, four] = await Promise.all([
fetchWithDuration(ctx.appPort, '/_next/image', query, opts),
fetchWithDuration(ctx.appPort, '/_next/image', query, opts),
])
- expect(three.duration).toBeLessThan(one.duration)
expect(three.res.status).toBe(200)
expect(three.res.headers.get('X-Nextjs-Cache')).toBe('STALE')
expect(three.res.headers.get('Content-Type')).toBe('image/webp')
@@ -1027,7 +1076,6 @@ export function runTests(ctx) {
`${contentDispositionType}; filename="test.webp"`
)
- expect(four.duration).toBeLessThan(one.duration)
expect(four.res.status).toBe(200)
expect(four.res.headers.get('X-Nextjs-Cache')).toBe('STALE')
expect(four.res.headers.get('Content-Type')).toBe('image/webp')
@@ -1069,7 +1117,7 @@ export function runTests(ctx) {
}
})
- if (ctx.dangerouslyAllowSVG) {
+ if (ctx.nextConfigImages?.dangerouslyAllowSVG) {
it('should use cached image file when parameters are the same for svg', async () => {
await cleanImagesDir(ctx)
@@ -1340,7 +1388,7 @@ export function runTests(ctx) {
}
}
-export const setupTests = (ctx) => {
+export const setupTests = (ctx: SetupTestsCtx) => {
const nextConfig = new File(join(ctx.appDir, 'next.config.js'))
describe('dev support w/o next.config.js', () => {
@@ -1349,7 +1397,7 @@ export const setupTests = (ctx) => {
return
}
const size = 384 // defaults defined in server/config.ts
- const curCtx = {
+ const curCtx: RunTestsCtx = {
...ctx,
w: size,
isDev: true,
@@ -1382,7 +1430,7 @@ export const setupTests = (ctx) => {
describe('dev support with next.config.js', () => {
const size = 400
- const curCtx = {
+ const curCtx: RunTestsCtx = {
...ctx,
w: size,
isDev: true,
@@ -1394,7 +1442,7 @@ export const setupTests = (ctx) => {
'assets.vercel.com',
'image-optimization-test.vercel.app',
],
- formats: ['image/avif', 'image/webp'],
+ formats: ['image/avif', 'image/webp'] as any,
deviceSizes: [largeSize],
imageSizes: [size],
...ctx.nextConfigImages,
@@ -1425,91 +1473,89 @@ export const setupTests = (ctx) => {
runTests(curCtx)
})
- ;(process.env.TURBOPACK_DEV ? describe.skip : describe)(
- 'Production Mode Server support w/o next.config.js',
- () => {
- if (ctx.nextConfigImages) {
- // skip this test because it requires next.config.js
- return
- }
- const size = 384 // defaults defined in server/config.ts
- const curCtx = {
- ...ctx,
- w: size,
- isDev: false,
- }
- beforeAll(async () => {
- const json = JSON.stringify({
- experimental: {
- outputFileTracingRoot: join(__dirname, '../../../..'),
- },
- })
- nextConfig.replace('{ /* replaceme */ }', json)
- curCtx.nextOutput = ''
- await nextBuild(curCtx.appDir)
- await cleanImagesDir(ctx)
- curCtx.appPort = await findPort()
- curCtx.app = await nextStart(curCtx.appDir, curCtx.appPort, {
- onStderr(msg) {
- curCtx.nextOutput += msg
- },
- cwd: curCtx.appDir,
- })
+ ;(process.env.TURBOPACK_DEV || process.env.TURBOPACK_BUILD
+ ? describe.skip
+ : describe)('Production Mode Server support w/o next.config.js', () => {
+ if (ctx.nextConfigImages) {
+ // skip this test because it requires next.config.js
+ return
+ }
+ const size = 384 // defaults defined in server/config.ts
+ const curCtx: RunTestsCtx = {
+ ...ctx,
+ w: size,
+ isDev: false,
+ }
+ beforeAll(async () => {
+ const json = JSON.stringify({
+ experimental: {
+ outputFileTracingRoot: join(__dirname, '../../../..'),
+ },
})
- afterAll(async () => {
- nextConfig.restore()
- if (curCtx.app) await killApp(curCtx.app)
+ nextConfig.replace('{ /* replaceme */ }', json)
+ curCtx.nextOutput = ''
+ await nextBuild(curCtx.appDir)
+ await cleanImagesDir(ctx)
+ curCtx.appPort = await findPort()
+ curCtx.app = await nextStart(curCtx.appDir, curCtx.appPort, {
+ onStderr(msg) {
+ curCtx.nextOutput += msg
+ },
+ cwd: curCtx.appDir,
})
+ })
+ afterAll(async () => {
+ nextConfig.restore()
+ if (curCtx.app) await killApp(curCtx.app)
+ })
- runTests(curCtx)
+ runTests(curCtx)
+ })
+ ;(process.env.TURBOPACK_DEV || process.env.TURBOPACK_BUILD
+ ? describe.skip
+ : describe)('Production Mode Server support with next.config.js', () => {
+ const size = 399
+ const curCtx: RunTestsCtx = {
+ ...ctx,
+ w: size,
+ isDev: false,
+ nextConfigImages: {
+ domains: [
+ 'localhost',
+ '127.0.0.1',
+ 'example.com',
+ 'assets.vercel.com',
+ 'image-optimization-test.vercel.app',
+ ],
+ formats: ['image/avif', 'image/webp'] as any,
+ deviceSizes: [size, largeSize],
+ ...ctx.nextConfigImages,
+ },
}
- )
- ;(process.env.TURBOPACK_DEV ? describe.skip : describe)(
- 'Production Mode Server support with next.config.js',
- () => {
- const size = 399
- const curCtx = {
- ...ctx,
- w: size,
- isDev: false,
- nextConfigImages: {
- domains: [
- 'localhost',
- '127.0.0.1',
- 'example.com',
- 'assets.vercel.com',
- 'image-optimization-test.vercel.app',
- ],
- formats: ['image/avif', 'image/webp'],
- deviceSizes: [size, largeSize],
- ...ctx.nextConfigImages,
+ beforeAll(async () => {
+ const json = JSON.stringify({
+ images: curCtx.nextConfigImages,
+ experimental: {
+ outputFileTracingRoot: join(__dirname, '../../../..'),
},
- }
- beforeAll(async () => {
- const json = JSON.stringify({
- images: curCtx.nextConfigImages,
- experimental: {
- outputFileTracingRoot: join(__dirname, '../../../..'),
- },
- })
- curCtx.nextOutput = ''
- nextConfig.replace('{ /* replaceme */ }', json)
- await nextBuild(curCtx.appDir)
- await cleanImagesDir(ctx)
- curCtx.appPort = await findPort()
- curCtx.app = await nextStart(curCtx.appDir, curCtx.appPort, {
- onStderr(msg) {
- curCtx.nextOutput += msg
- },
- cwd: curCtx.appDir,
- })
})
- afterAll(async () => {
- nextConfig.restore()
- if (curCtx.app) await killApp(curCtx.app)
+ curCtx.nextOutput = ''
+ nextConfig.replace('{ /* replaceme */ }', json)
+ await nextBuild(curCtx.appDir)
+ await cleanImagesDir(ctx)
+ curCtx.appPort = await findPort()
+ curCtx.app = await nextStart(curCtx.appDir, curCtx.appPort, {
+ onStderr(msg) {
+ curCtx.nextOutput += msg
+ },
+ cwd: curCtx.appDir,
})
+ })
+ afterAll(async () => {
+ nextConfig.restore()
+ if (curCtx.app) await killApp(curCtx.app)
+ })
- runTests(curCtx)
- }
- )
+ runTests(curCtx)
+ })
}
diff --git a/test/turbopack-build-tests-manifest.json b/test/turbopack-build-tests-manifest.json
index 38e08bbbba840..861b17c3df266 100644
--- a/test/turbopack-build-tests-manifest.json
+++ b/test/turbopack-build-tests-manifest.json
@@ -10842,12 +10842,133 @@
"with contentDispositionType attachment dev support with next.config.js should use cached image file when parameters are the same for animated gif"
],
"failed": [
+ "with contentDispositionType attachment Production Mode Server support w/o next.config.js should set cache-control to immutable for static images",
"with contentDispositionType attachment Production Mode Server support with next.config.js should set cache-control to immutable for static images"
],
"pending": [],
"flakey": [],
"runtimeError": false
},
+ "test/integration/image-optimizer/test/dangerously-allow-svg.test.ts": {
+ "passed": [
+ "with dangerouslyAllowSVG config Production Mode Server support with next.config.js should automatically detect image type when content-type is octet-stream",
+ "with dangerouslyAllowSVG config Production Mode Server support with next.config.js should compress avif smaller than webp at q=100",
+ "with dangerouslyAllowSVG config Production Mode Server support with next.config.js should compress avif smaller than webp at q=50",
+ "with dangerouslyAllowSVG config Production Mode Server support with next.config.js should compress avif smaller than webp at q=75",
+ "with dangerouslyAllowSVG config Production Mode Server support with next.config.js should downlevel avif format to jpeg for old Safari",
+ "with dangerouslyAllowSVG config Production Mode Server support with next.config.js should downlevel webp format to jpeg for old Safari",
+ "with dangerouslyAllowSVG config Production Mode Server support with next.config.js should emit blur svg when width is 8 in dev but not prod",
+ "with dangerouslyAllowSVG config Production Mode Server support with next.config.js should emit blur svg when width is less than 8 in dev but not prod",
+ "with dangerouslyAllowSVG config Production Mode Server support with next.config.js should error if the image file does not exist",
+ "with dangerouslyAllowSVG config Production Mode Server support with next.config.js should error if the resource isn't a valid image",
+ "with dangerouslyAllowSVG config Production Mode Server support with next.config.js should fail when domain is not defined in next.config.js",
+ "with dangerouslyAllowSVG config Production Mode Server support with next.config.js should fail when internal url is not an image",
+ "with dangerouslyAllowSVG config Production Mode Server support with next.config.js should fail when q is greater than 100",
+ "with dangerouslyAllowSVG config Production Mode Server support with next.config.js should fail when q is less than 1",
+ "with dangerouslyAllowSVG config Production Mode Server support with next.config.js should fail when q is missing",
+ "with dangerouslyAllowSVG config Production Mode Server support with next.config.js should fail when q is not a number",
+ "with dangerouslyAllowSVG config Production Mode Server support with next.config.js should fail when url fails to load an image",
+ "with dangerouslyAllowSVG config Production Mode Server support with next.config.js should fail when url has file protocol",
+ "with dangerouslyAllowSVG config Production Mode Server support with next.config.js should fail when url has ftp protocol",
+ "with dangerouslyAllowSVG config Production Mode Server support with next.config.js should fail when url is missing",
+ "with dangerouslyAllowSVG config Production Mode Server support with next.config.js should fail when w is 0",
+ "with dangerouslyAllowSVG config Production Mode Server support with next.config.js should fail when w is less than 0",
+ "with dangerouslyAllowSVG config Production Mode Server support with next.config.js should fail when w is missing",
+ "with dangerouslyAllowSVG config Production Mode Server support with next.config.js should fail when w is not a number",
+ "with dangerouslyAllowSVG config Production Mode Server support with next.config.js should fail when width is not in next.config.js",
+ "with dangerouslyAllowSVG config Production Mode Server support with next.config.js should handle concurrent requests",
+ "with dangerouslyAllowSVG config Production Mode Server support with next.config.js should handle non-ascii characters in image url",
+ "with dangerouslyAllowSVG config Production Mode Server support with next.config.js should maintain animated gif",
+ "with dangerouslyAllowSVG config Production Mode Server support with next.config.js should maintain animated png",
+ "with dangerouslyAllowSVG config Production Mode Server support with next.config.js should maintain animated png 2",
+ "with dangerouslyAllowSVG config Production Mode Server support with next.config.js should maintain animated webp",
+ "with dangerouslyAllowSVG config Production Mode Server support with next.config.js should maintain bmp",
+ "with dangerouslyAllowSVG config Production Mode Server support with next.config.js should maintain ico format",
+ "with dangerouslyAllowSVG config Production Mode Server support with next.config.js should maintain jpg format for old Safari",
+ "with dangerouslyAllowSVG config Production Mode Server support with next.config.js should maintain png format for old Safari",
+ "with dangerouslyAllowSVG config Production Mode Server support with next.config.js should normalize invalid status codes",
+ "with dangerouslyAllowSVG config Production Mode Server support with next.config.js should not allow svg with application header",
+ "with dangerouslyAllowSVG config Production Mode Server support with next.config.js should not allow svg with comma header",
+ "with dangerouslyAllowSVG config Production Mode Server support with next.config.js should not allow svg with uppercase header",
+ "with dangerouslyAllowSVG config Production Mode Server support with next.config.js should not allow vector svg",
+ "with dangerouslyAllowSVG config Production Mode Server support with next.config.js should not resize if requested width is larger than original source image",
+ "with dangerouslyAllowSVG config Production Mode Server support with next.config.js should resize absolute url from localhost",
+ "with dangerouslyAllowSVG config Production Mode Server support with next.config.js should resize relative url and new Chrome accept header as avif",
+ "with dangerouslyAllowSVG config Production Mode Server support with next.config.js should resize relative url and old Chrome accept header as webp",
+ "with dangerouslyAllowSVG config Production Mode Server support with next.config.js should resize relative url and png accept header",
+ "with dangerouslyAllowSVG config Production Mode Server support with next.config.js should resize relative url and webp Firefox accept header",
+ "with dangerouslyAllowSVG config Production Mode Server support with next.config.js should resize relative url with invalid accept header as gif",
+ "with dangerouslyAllowSVG config Production Mode Server support with next.config.js should resize relative url with invalid accept header as png",
+ "with dangerouslyAllowSVG config Production Mode Server support with next.config.js should resize relative url with invalid accept header as tiff",
+ "with dangerouslyAllowSVG config Production Mode Server support with next.config.js should return home page",
+ "with dangerouslyAllowSVG config Production Mode Server support with next.config.js should set 304 status without body when etag matches if-none-match",
+ "with dangerouslyAllowSVG config Production Mode Server support with next.config.js should use cache and stale-while-revalidate when query is the same for external image",
+ "with dangerouslyAllowSVG config Production Mode Server support with next.config.js should use cache and stale-while-revalidate when query is the same for internal image",
+ "with dangerouslyAllowSVG config Production Mode Server support with next.config.js should use cached image file when parameters are the same for animated gif",
+ "with dangerouslyAllowSVG config dev support with next.config.js should automatically detect image type when content-type is octet-stream",
+ "with dangerouslyAllowSVG config dev support with next.config.js should compress avif smaller than webp at q=100",
+ "with dangerouslyAllowSVG config dev support with next.config.js should compress avif smaller than webp at q=50",
+ "with dangerouslyAllowSVG config dev support with next.config.js should compress avif smaller than webp at q=75",
+ "with dangerouslyAllowSVG config dev support with next.config.js should downlevel avif format to jpeg for old Safari",
+ "with dangerouslyAllowSVG config dev support with next.config.js should downlevel webp format to jpeg for old Safari",
+ "with dangerouslyAllowSVG config dev support with next.config.js should emit blur svg when width is 8 in dev but not prod",
+ "with dangerouslyAllowSVG config dev support with next.config.js should emit blur svg when width is less than 8 in dev but not prod",
+ "with dangerouslyAllowSVG config dev support with next.config.js should error if the image file does not exist",
+ "with dangerouslyAllowSVG config dev support with next.config.js should error if the resource isn't a valid image",
+ "with dangerouslyAllowSVG config dev support with next.config.js should fail when domain is not defined in next.config.js",
+ "with dangerouslyAllowSVG config dev support with next.config.js should fail when internal url is not an image",
+ "with dangerouslyAllowSVG config dev support with next.config.js should fail when q is greater than 100",
+ "with dangerouslyAllowSVG config dev support with next.config.js should fail when q is less than 1",
+ "with dangerouslyAllowSVG config dev support with next.config.js should fail when q is missing",
+ "with dangerouslyAllowSVG config dev support with next.config.js should fail when q is not a number",
+ "with dangerouslyAllowSVG config dev support with next.config.js should fail when url fails to load an image",
+ "with dangerouslyAllowSVG config dev support with next.config.js should fail when url has file protocol",
+ "with dangerouslyAllowSVG config dev support with next.config.js should fail when url has ftp protocol",
+ "with dangerouslyAllowSVG config dev support with next.config.js should fail when url is missing",
+ "with dangerouslyAllowSVG config dev support with next.config.js should fail when w is 0",
+ "with dangerouslyAllowSVG config dev support with next.config.js should fail when w is less than 0",
+ "with dangerouslyAllowSVG config dev support with next.config.js should fail when w is missing",
+ "with dangerouslyAllowSVG config dev support with next.config.js should fail when w is not a number",
+ "with dangerouslyAllowSVG config dev support with next.config.js should fail when width is not in next.config.js",
+ "with dangerouslyAllowSVG config dev support with next.config.js should handle concurrent requests",
+ "with dangerouslyAllowSVG config dev support with next.config.js should handle non-ascii characters in image url",
+ "with dangerouslyAllowSVG config dev support with next.config.js should maintain animated gif",
+ "with dangerouslyAllowSVG config dev support with next.config.js should maintain animated png",
+ "with dangerouslyAllowSVG config dev support with next.config.js should maintain animated png 2",
+ "with dangerouslyAllowSVG config dev support with next.config.js should maintain animated webp",
+ "with dangerouslyAllowSVG config dev support with next.config.js should maintain bmp",
+ "with dangerouslyAllowSVG config dev support with next.config.js should maintain ico format",
+ "with dangerouslyAllowSVG config dev support with next.config.js should maintain jpg format for old Safari",
+ "with dangerouslyAllowSVG config dev support with next.config.js should maintain png format for old Safari",
+ "with dangerouslyAllowSVG config dev support with next.config.js should normalize invalid status codes",
+ "with dangerouslyAllowSVG config dev support with next.config.js should not allow svg with application header",
+ "with dangerouslyAllowSVG config dev support with next.config.js should not allow svg with comma header",
+ "with dangerouslyAllowSVG config dev support with next.config.js should not allow svg with uppercase header",
+ "with dangerouslyAllowSVG config dev support with next.config.js should not allow vector svg",
+ "with dangerouslyAllowSVG config dev support with next.config.js should not resize if requested width is larger than original source image",
+ "with dangerouslyAllowSVG config dev support with next.config.js should resize absolute url from localhost",
+ "with dangerouslyAllowSVG config dev support with next.config.js should resize relative url and new Chrome accept header as avif",
+ "with dangerouslyAllowSVG config dev support with next.config.js should resize relative url and old Chrome accept header as webp",
+ "with dangerouslyAllowSVG config dev support with next.config.js should resize relative url and png accept header",
+ "with dangerouslyAllowSVG config dev support with next.config.js should resize relative url and webp Firefox accept header",
+ "with dangerouslyAllowSVG config dev support with next.config.js should resize relative url with invalid accept header as gif",
+ "with dangerouslyAllowSVG config dev support with next.config.js should resize relative url with invalid accept header as png",
+ "with dangerouslyAllowSVG config dev support with next.config.js should resize relative url with invalid accept header as tiff",
+ "with dangerouslyAllowSVG config dev support with next.config.js should return home page",
+ "with dangerouslyAllowSVG config dev support with next.config.js should set 304 status without body when etag matches if-none-match",
+ "with dangerouslyAllowSVG config dev support with next.config.js should set cache-control to immutable for static images",
+ "with dangerouslyAllowSVG config dev support with next.config.js should use cache and stale-while-revalidate when query is the same for external image",
+ "with dangerouslyAllowSVG config dev support with next.config.js should use cache and stale-while-revalidate when query is the same for internal image",
+ "with dangerouslyAllowSVG config dev support with next.config.js should use cached image file when parameters are the same for animated gif"
+ ],
+ "failed": [
+ "with dangerouslyAllowSVG config Production Mode Server support w/o next.config.js should set cache-control to immutable for static images",
+ "with dangerouslyAllowSVG config Production Mode Server support with next.config.js should set cache-control to immutable for static images"
+ ],
+ "pending": [],
+ "flakey": [],
+ "runtimeError": false
+ },
"test/integration/image-optimizer/test/index.test.ts": {
"passed": [
"Image Optimizer External rewrite support with for serving static content in images production mode should return response when image is served from an external rewrite",
@@ -10995,6 +11116,7 @@
"with minimumCacheTTL of 5 sec dev support with next.config.js should use cached image file when parameters are the same for animated gif"
],
"failed": [
+ "with minimumCacheTTL of 5 sec Production Mode Server support w/o next.config.js should set cache-control to immutable for static images",
"with minimumCacheTTL of 5 sec Production Mode Server support with next.config.js should set cache-control to immutable for static images"
],
"pending": [],
diff --git a/test/unit/image-optimizer/detect-content-type.test.ts b/test/unit/image-optimizer/detect-content-type.test.ts
index 24df3cd9a48d3..d8731cca0096a 100644
--- a/test/unit/image-optimizer/detect-content-type.test.ts
+++ b/test/unit/image-optimizer/detect-content-type.test.ts
@@ -22,6 +22,10 @@ describe('detectContentType', () => {
const buffer = await getImage('./images/test.svg')
expect(detectContentType(buffer)).toBe('image/svg+xml')
})
+ it('should return svg for inline svg', async () => {
+ const buffer = await getImage('./images/test-inline.svg')
+ expect(detectContentType(buffer)).toBe('image/svg+xml')
+ })
it('should return avif', async () => {
const buffer = await getImage('./images/test.avif')
expect(detectContentType(buffer)).toBe('image/avif')
diff --git a/test/unit/image-optimizer/images/test-inline.svg b/test/unit/image-optimizer/images/test-inline.svg
new file mode 100644
index 0000000000000..811eeaf6b154e
--- /dev/null
+++ b/test/unit/image-optimizer/images/test-inline.svg
@@ -0,0 +1,3 @@
+
\ No newline at end of file