vercel / next.js

The React Framework
https://nextjs.org
MIT License
126.94k stars 26.98k forks source link

Image with static import throws an error with Vitest #45350

Open lucas-santosP opened 1 year ago

lucas-santosP commented 1 year ago

Verify canary release

Provide environment information

Operating System:
  Platform: win32
  Arch: x64
  Version: Windows 10 Pro
Binaries:
  Node: 18.13.0
  npm: N/A
  Yarn: N/A
  pnpm: N/A
Relevant packages:
  next: 13.1.6-canary.2
  eslint-config-next: N/A
  react: 18.2.0
  react-dom: 18.2.0

Which area(s) of Next.js are affected? (leave empty if unsure)

No response

Link to the code that reproduces this issue

https://github.com/lucas-santosP/next-image-static-import-with-vitest

To Reproduce

After following the Vitest setup in the example with-vitest, to reproduce the error you simply need to add an Image component with static import and remove the width and height props.

For example:

// On top of the file:
import vercelLogo from "../public/vercel.svg";

// [...] In the component
<Image src={vercelLogo} alt="Vercel Logo" />

And then run the tests

yarn vitest

Describe the Bug

it seems that the Vitest running environment does not recognize the static import in the Image component, so when I try to run the test it throws an error saying that width and height are required:

FAIL  __tests__/Home.test.tsx > home
Error: Image with src "/public/vercel.svg" is missing required "height" property.

FAIL  __tests__/Home.test.tsx > home
Error: Image with src "/public/vercel.svg" is missing required "width" property.

The same happens with next/legacy/image and using in Next 12.x too.

Expected Behavior

Infer that the image has static import so it does not need width and height props anymore.

Which browser are you using? (if relevant)

No response

How are you deploying your application? (if relevant)

No response

Lihemen commented 1 year ago

Is there any update on this issue?

Same thing experienced.

Operating System: Platform: MacOS Version: Ventura 13.4.1 Binaries: Node: 18.12.1 npm: 8.19.2 Yarn: N/A pnpm: N/A Relevant packages: next: 13.4.8-canary.2 vitest: 0.33.0 react: 18.2.0 react-dom: 18.2.0 jsdom: 22.1.0

KoichiKiyokawa commented 1 year ago

@lucas-santosP @Lihemen

I've found a workaround for this issue.

The difference between Vite and Next.js (with Webpack) in terms of static asset import is as follows:

import vercelLogo from "../public/vercel.svg";
// In Vite: vercelLogo is a string
// In Next.js: vercelLogo is an object { src: string; width: number; height: number}

To simulate the behavior of Next.js in Vite, you will need to write a Vite plugin, as shown below:

// vite.config.ts or vitest.config.ts
import { defineConfig } from 'vitest/config';
import react from '@vitejs/plugin-react';
import path from 'path';

export default defineConfig({
  plugins: [react(), stubNextAssetImport()],
  test: {
    environment: 'jsdom',
  },
});

function stubNextAssetImport() {
  return {
    name: 'stub-next-asset-import',
    transform(_code: string, id: string) {
      if (/(jpg|jpeg|png|webp|gif|svg)$/.test(id)) {
        const imgSrc = path.relative(process.cwd(), id);
        return {
          code: `export default { src: '${imgSrc}', height: 1, width: 1 }`,
        };
      }
    },
  };
}

You can try it here: https://stackblitz.com/edit/node-h5kfn3?file=vitest.config.ts

AlmogH commented 11 months ago

Thanks @KoichiKiyokawa the workaround helped me a lot!! Adding on it I checked next/jest config and they have a mock in jest for it as follows:

moduleNameMapper: {
    '^.+\\.module\\.(css|sass|scss)$': '/next/dist/build/jest/object-proxy.js',
    '^.+\\.(css|sass|scss)$': '/next/dist/build/jest/__mocks__/styleMock.js',
    '^.+\\.(png|jpg|jpeg|gif|webp|avif|ico|bmp)$': '/next/dist/build/jest/__mocks__/fileMock.js',
    '^.+\\.(svg)$': '/next/dist/build/jest/__mocks__/fileMock.js',
    '@next/font/(.*)': '/next/dist/build/jest/__mocks__/nextFontMock.js',
    'next/font/(.*)': '/next/dist/build/jest/__mocks__/nextFontMock.js',
    'server-only': '/next/dist/build/jest/__mocks__/empty.js'
}

and you can find the mocks here: next/jest mocks

@lucas-santosP @Lihemen If you want to change the workaround to work as next/jest did it

tyteen4a03 commented 1 month ago

@lucas-santosP @Lihemen

I've found a workaround for this issue.

The difference between Vite and Next.js (with Webpack) in terms of static asset import is as follows:

import vercelLogo from "../public/vercel.svg";
// In Vite: vercelLogo is a string
// In Next.js: vercelLogo is an object { src: string; width: number; height: number}

To simulate the behavior of Next.js in Vite, you will need to write a Vite plugin, as shown below:

// vite.config.ts or vitest.config.ts
import { defineConfig } from 'vitest/config';
import react from '@vitejs/plugin-react';
import path from 'path';

export default defineConfig({
  plugins: [react(), stubNextAssetImport()],
  test: {
    environment: 'jsdom',
  },
});

function stubNextAssetImport() {
  return {
    name: 'stub-next-asset-import',
    transform(_code: string, id: string) {
      if (/(jpg|jpeg|png|webp|gif|svg)$/.test(id)) {
        const imgSrc = path.relative(process.cwd(), id);
        return {
          code: `export default { src: '${imgSrc}', height: 1, width: 1 }`,
        };
      }
    },
  };
}

You can try it here: https://stackblitz.com/edit/node-h5kfn3?file=vitest.config.ts

For me, since I don't care about the output image, I changed the image output to:

                return {
                    code: `export default { src: 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAQAAAC1HAwCAAAAC0lEQVR42mNkYAAAAAYAAjCB0C8AAAAASUVORK5CYII=', height: 1, width: 1 }`,
                };

which is a 1x1 transparent image.