diff --git a/packages/next/client/future/image.tsx b/packages/next/client/future/image.tsx index 4ca2d06ec3c55c7..f3ffe5b7033edb2 100644 --- a/packages/next/client/future/image.tsx +++ b/packages/next/client/future/image.tsx @@ -591,8 +591,11 @@ export default function Image({ blurDataURL = blurDataURL || staticImageData.blurDataURL staticSrc = staticImageData.src - height = height || staticImageData.height - width = width || staticImageData.width + // Ignore width and height (come from the bundler) when "fill" is used + if (!fill) { + height = height || staticImageData.height + width = width || staticImageData.width + } if (!staticImageData.height || !staticImageData.width) { throw new Error( `An object should only be passed to the image component src parameter if it comes from a static image import. It must include height and width. Received ${JSON.stringify( diff --git a/test/integration/image-future/default/pages/static-img.js b/test/integration/image-future/default/pages/static-img.js index d5ce76c1289809b..6b6b4cfd5739e33 100644 --- a/test/integration/image-future/default/pages/static-img.js +++ b/test/integration/image-future/default/pages/static-img.js @@ -36,6 +36,10 @@ const Page = () => { + + + +
diff --git a/test/integration/image-future/default/test/static.test.ts b/test/integration/image-future/default/test/static.test.ts index 7e02bf407b2b8d0..e40d548ba17d7bd 100644 --- a/test/integration/image-future/default/test/static.test.ts +++ b/test/integration/image-future/default/test/static.test.ts @@ -6,6 +6,7 @@ import { renderViaHTTP, File, waitFor, + launchApp, } from 'next-test-utils' import webdriver from 'next-webdriver' import { join } from 'path' @@ -18,7 +19,7 @@ let html const indexPage = new File(join(appDir, 'pages/static-img.js')) -const runTests = () => { +const runTests = (isDev = false) => { it('Should allow an image with a static src to omit height and width', async () => { expect(await browser.elementById('basic-static')).toBeTruthy() expect(await browser.elementById('blur-png')).toBeTruthy() @@ -29,44 +30,62 @@ const runTests = () => { expect(await browser.elementById('static-gif')).toBeTruthy() expect(await browser.elementById('static-bmp')).toBeTruthy() expect(await browser.elementById('static-ico')).toBeTruthy() + expect(await browser.elementById('static-svg-fill')).toBeTruthy() + expect(await browser.elementById('static-gif-fill')).toBeTruthy() + expect(await browser.elementById('static-bmp-fill')).toBeTruthy() + expect(await browser.elementById('static-ico-fill')).toBeTruthy() expect(await browser.elementById('static-unoptimized')).toBeTruthy() }) - it('Should use immutable cache-control header for static import', async () => { - await browser.eval( - `document.getElementById("basic-static").scrollIntoView()` - ) - await waitFor(1000) - const url = await browser.eval( - `document.getElementById("basic-static").src` - ) - const res = await fetch(url) - expect(res.headers.get('cache-control')).toBe( - 'public, max-age=315360000, immutable' - ) - }) - it('Should use immutable cache-control header even when unoptimized', async () => { - await browser.eval( - `document.getElementById("static-unoptimized").scrollIntoView()` - ) - await waitFor(1000) - const url = await browser.eval( - `document.getElementById("static-unoptimized").src` - ) - const res = await fetch(url) - expect(res.headers.get('cache-control')).toBe( - 'public, max-age=31536000, immutable' - ) - }) + + if (!isDev) { + // cache-control is set to "0, no-store" in dev mode + it('Should use immutable cache-control header for static import', async () => { + await browser.eval( + `document.getElementById("basic-static").scrollIntoView()` + ) + await waitFor(1000) + const url = await browser.eval( + `document.getElementById("basic-static").src` + ) + const res = await fetch(url) + expect(res.headers.get('cache-control')).toBe( + 'public, max-age=315360000, immutable' + ) + }) + + it('Should use immutable cache-control header even when unoptimized', async () => { + await browser.eval( + `document.getElementById("static-unoptimized").scrollIntoView()` + ) + await waitFor(1000) + const url = await browser.eval( + `document.getElementById("static-unoptimized").src` + ) + const res = await fetch(url) + expect(res.headers.get('cache-control')).toBe( + 'public, max-age=31536000, immutable' + ) + }) + } it('Should automatically provide an image height and width', async () => { expect(html).toContain('width="400" height="300"') }) it('Should allow provided width and height to override intrinsic', async () => { expect(html).toContain('width="150" height="150"') }) + it('Should add a blur to a statically imported image in "raw" mode', async () => { - expect(html).toContain( - `style="background-size:cover;background-position:0% 0%;background-image:url("data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http%3A//www.w3.org/2000/svg' viewBox='0 0 400 300'%3E%3Cfilter id='b' color-interpolation-filters='sRGB'%3E%3CfeGaussianBlur stdDeviation='50'/%3E%3C/filter%3E%3Cimage filter='url(%23b)' x='0' y='0' height='100%25' width='100%25' href='data:image/jpeg;base64,/9j/4AAQSkZJRgABAQAAAQABAAD/2wCEAAoKCgoKCgsMDAsPEA4QDxYUExMUFiIYGhgaGCIzICUgICUgMy03LCksNy1RQDg4QFFeT0pPXnFlZXGPiI+7u/sBCgoKCgoKCwwMCw8QDhAPFhQTExQWIhgaGBoYIjMgJSAgJSAzLTcsKSw3LVFAODhAUV5PSk9ecWVlcY+Ij7u7+//CABEIAAYACAMBIgACEQEDEQH/xAAnAAEBAAAAAAAAAAAAAAAAAAAABwEBAAAAAAAAAAAAAAAAAAAAAP/aAAwDAQACEAMQAAAAmgP/xAAcEAACAQUBAAAAAAAAAAAAAAASFBMAAQMFERX/2gAIAQEAAT8AZ1HjrKZX55JysIc4Ff/EABQRAQAAAAAAAAAAAAAAAAAAAAD/2gAIAQIBAT8Af//EABQRAQAAAAAAAAAAAAAAAAAAAAD/2gAIAQMBAT8Af//Z'/%3E%3C/svg%3E")` - ) + // next-image-loader uses different blur behavior for dev/prod mode + // See next/build/webpack/loaders/next-image-loader + if (isDev) { + expect(html).toContain( + `style="background-size:cover;background-position:0% 0%;filter:blur(20px);background-image:url("/_next/image?url=%2F_next%2Fstatic%2Fmedia%2Ftest-rect.f323a148.jpg&w=8&q=70")"` + ) + } else { + expect(html).toContain( + `style="background-size:cover;background-position:0% 0%;background-image:url("data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http%3A//www.w3.org/2000/svg' viewBox='0 0 400 300'%3E%3Cfilter id='b' color-interpolation-filters='sRGB'%3E%3CfeGaussianBlur stdDeviation='50'/%3E%3C/filter%3E%3Cimage filter='url(%23b)' x='0' y='0' height='100%25' width='100%25' href='data:image/jpeg;base64,/9j/4AAQSkZJRgABAQAAAQABAAD/2wCEAAoKCgoKCgsMDAsPEA4QDxYUExMUFiIYGhgaGCIzICUgICUgMy03LCksNy1RQDg4QFFeT0pPXnFlZXGPiI+7u/sBCgoKCgoKCwwMCw8QDhAPFhQTExQWIhgaGBoYIjMgJSAgJSAzLTcsKSw3LVFAODhAUV5PSk9ecWVlcY+Ij7u7+//CABEIAAYACAMBIgACEQEDEQH/xAAnAAEBAAAAAAAAAAAAAAAAAAAABwEBAAAAAAAAAAAAAAAAAAAAAP/aAAwDAQACEAMQAAAAmgP/xAAcEAACAQUBAAAAAAAAAAAAAAASFBMAAQMFERX/2gAIAQEAAT8AZ1HjrKZX55JysIc4Ff/EABQRAQAAAAAAAAAAAAAAAAAAAAD/2gAIAQIBAT8Af//EABQRAQAAAAAAAAAAAAAAAAAAAAD/2gAIAQMBAT8Af//Z'/%3E%3C/svg%3E")` + ) + } }) } @@ -90,15 +109,30 @@ describe('Build Error Tests', () => { }) }) describe('Future Static Image Component Tests', () => { - beforeAll(async () => { - await nextBuild(appDir) - appPort = await findPort() - app = await nextStart(appDir, appPort) - html = await renderViaHTTP(appPort, '/static-img') - browser = await webdriver(appPort, '/static-img') + describe('production mode', () => { + beforeAll(async () => { + await nextBuild(appDir) + appPort = await findPort() + app = await nextStart(appDir, appPort) + html = await renderViaHTTP(appPort, '/static-img') + browser = await webdriver(appPort, '/static-img') + }) + afterAll(() => { + killApp(app) + }) + runTests(false) }) - afterAll(() => { - killApp(app) + + describe('dev mode', () => { + beforeAll(async () => { + appPort = await findPort() + app = await launchApp(appDir, appPort) + html = await renderViaHTTP(appPort, '/static-img') + browser = await webdriver(appPort, '/static-img') + }) + afterAll(() => { + killApp(app) + }) + runTests(true) }) - runTests() })