mirror of
https://github.com/hanzoai/next.js.git
synced 2026-10-30 14:46:38 +00:00
- Fixes #89492 - Fixes #18415 ## Problem When testing Next.js <Image /> components with Jest, custom images.qualities configured in next.config.js are ignored, causing: - Image URLs to use default quality (q=75) instead of configured values - Warning: "Image with src ... is using quality X which is not configured in images.qualities [75]" ### Root Cause In production and development, Next.js uses webpack's DefinePlugin to inject the image configuration via process.env.__NEXT_IMAGE_OPTS. However, *Jest tests don't use webpack*, so process.env.__NEXT_IMAGE_OPTS is undefined, causing the Image component to fall back to default configuration. ## Solution This PR implements a two-part fix: 1. *Jest globals population* (packages/next/src/build/jest/jest.ts): - Expose nextConfig.images via Jest globals.__NEXT_IMAGE_OPTS 2. *Runtime fallback* (image components): - When process.env.__NEXT_IMAGE_OPTS is undefined, fall back to globalThis.__NEXT_IMAGE_OPTS - Applied to: - packages/next/src/client/image-component.tsx - packages/next/src/shared/lib/image-external.tsx - packages/next/src/client/legacy/image.tsx ## Changes - ✅ Minimal, targeted fix (4 files, ~40 lines) - ✅ No production/development behavior changes - ✅ No test-only conditionals or environment checks - ✅ Comprehensive test coverage added ## Testing Added test in test/production/app-dir/image-jest-qualities/ that: - Configures custom qualities [90, 100] in next.config.js - Renders <Image quality={100} /> in Jest with jsdom - Asserts generated image URLs contain q=100 not q=75 Test correctly *fails on canary* (shows warning about unconfigured quality) and *passes with fix*. ## Verification - ✅ New test passes with fix - ✅ Existing image tests unaffected - ✅ Addresses original issue reproduction exactly --------- Co-authored-by: Steven <[email protected]>
115 lines
2.8 KiB
TypeScript
115 lines
2.8 KiB
TypeScript
import { createNext, FileRef } from 'e2e-utils'
|
|
import { NextInstance } from 'e2e-utils'
|
|
import path from 'path'
|
|
import execa from 'execa'
|
|
|
|
const appDir = path.join(__dirname, 'app')
|
|
|
|
describe('next/jest image qualities config', () => {
|
|
let next: NextInstance
|
|
|
|
beforeAll(async () => {
|
|
next = await createNext({
|
|
skipStart: true,
|
|
files: {
|
|
'next.config.js': `
|
|
module.exports = {
|
|
images: {
|
|
qualities: [90, 100],
|
|
},
|
|
}
|
|
`,
|
|
app: new FileRef(path.join(appDir, 'app')),
|
|
'jest.config.js': `
|
|
const nextJest = require('next/jest')
|
|
|
|
const createJestConfig = nextJest({
|
|
dir: './',
|
|
})
|
|
|
|
const customJestConfig = {
|
|
testEnvironment: 'jsdom',
|
|
}
|
|
|
|
module.exports = createJestConfig(customJestConfig)
|
|
`,
|
|
[`tests/image.test.tsx`]: `
|
|
import Image from 'next/image'
|
|
import { render, screen } from '@testing-library/react'
|
|
|
|
describe('Image quality config', () => {
|
|
it('respects custom qualities from next.config.js', () => {
|
|
render(
|
|
<Image
|
|
src="/test.jpg"
|
|
alt="test"
|
|
width={500}
|
|
height={500}
|
|
quality={100}
|
|
/>
|
|
)
|
|
|
|
const img = screen.getByRole('img')
|
|
|
|
// Should use quality=100 from our config, not default q=75
|
|
expect(img.getAttribute('src')).toContain('q=100')
|
|
expect(img.getAttribute('src')).not.toContain('q=75')
|
|
})
|
|
|
|
it('uses custom qualities for srcset generation', () => {
|
|
render(
|
|
<Image
|
|
src="/test.jpg"
|
|
alt="test"
|
|
width={500}
|
|
height={500}
|
|
sizes="100vw"
|
|
/>
|
|
)
|
|
|
|
const img = screen.getByRole('img')
|
|
|
|
// Should generate srcset with our custom qualities [90, 100]
|
|
// and not include default quality 75
|
|
const srcSet = img.getAttribute('srcset') || ''
|
|
const srcSetEntries = srcSet.split(',')
|
|
const hasCustomQualities = srcSetEntries.some(entry =>
|
|
entry.includes('q=90') || entry.includes('q=100')
|
|
)
|
|
const hasDefaultQuality = srcSetEntries.some(entry =>
|
|
entry.includes('q=75')
|
|
)
|
|
|
|
expect(hasCustomQualities).toBe(true)
|
|
expect(hasDefaultQuality).toBe(false)
|
|
})
|
|
})
|
|
`,
|
|
},
|
|
dependencies: {
|
|
jest: '29.7.0',
|
|
'jest-environment-jsdom': '29.7.0',
|
|
'@testing-library/react': '15.0.2',
|
|
'@testing-library/jest-dom': '5.17.0',
|
|
},
|
|
})
|
|
})
|
|
|
|
afterAll(() => next.destroy())
|
|
|
|
it('should pass jest tests with custom image qualities', async () => {
|
|
const result = await execa(
|
|
'pnpm',
|
|
['jest', 'tests/image.test.tsx', '--forceExit', '--verbose'],
|
|
{
|
|
cwd: next.testDir,
|
|
reject: false,
|
|
}
|
|
)
|
|
const output = result.stdout || result.stderr || ''
|
|
console.log('Jest output:', output)
|
|
expect(output).toContain('PASS')
|
|
expect(output).toMatch(/2 passed/)
|
|
})
|
|
})
|