Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Next.js Image does not render image under Cypress Component test #18064

Closed
TimPchelintsev opened this issue Sep 11, 2021 · 14 comments
Closed

Next.js Image does not render image under Cypress Component test #18064

TimPchelintsev opened this issue Sep 11, 2021 · 14 comments
Labels
CT Issue related to component testing

Comments

@TimPchelintsev
Copy link

Current behavior

I have set up Cypress component testing for Next.js project which make heavy use of next/Image component.
But Next.js dev server launched by Cypress returns 404: Cannot GET /_next/image

Desired behavior

It will be cool to see next.js Image component working.

Test code to reproduce

TO DO

Cypress Version

8.3.1

Other

No response

@andrewagain
Copy link

andrewagain commented Dec 10, 2021

In addition to using next/Image I have also tried:

  • Importing an image
  • Loading an image from ./public with "fileServerFolder": "public"

Neither of these approaches work. Is there ANY way to get cypress component tests to render a local image file?

@andrewagain
Copy link

FYI, here is another issue related to images in component tests: #16964

@corelmax
Copy link

corelmax commented Mar 2, 2022

Hi @ahfarmer!

I'm running into the same problem. Did you find any solution or workaround for this?

@ifeanyiisitor
Copy link

Also running into this issue.

@boylec
Copy link

boylec commented Apr 27, 2022

Same, but its related to trying to use mockServiceWorker.js (which is in my /public folder) while running component tests.

Seems like fileServerFolder: "public" in cypress.json doesn't really do anything for component tests?

@tschmidt64-SC
Copy link

I am also running into this issue.

@lmiller1990 lmiller1990 added CT Issue related to component testing and removed component testing labels Aug 15, 2022
@Ilyeo
Copy link

Ilyeo commented Oct 7, 2022

I had the same issue

Using next/image component I'm getting an load resource error Cannot GET /_next/image

A workaround that I found is passing unoptimized prop

<Image ... unoptimized />

That shows the image in the cypress component environment

This workaround does not like me and I do not recommend it, but it probably gives us a hint of the cause

@cdierkens
Copy link

Should have commented this a while ago, but here's how I solved the issue.

This approach replaces the next/image module with a mock.

    // webpack.config.js
    // Replace the `next/image` component with a standard HTML `img` element.
    webpackConfig.plugins.push(
      new webpack.NormalModuleReplacementPlugin(
        /next\/image/,
        require.resolve(path.join(__dirname, '..', '..', '__mocks__', 'next', 'image'))
      )
    );
// __mocks__/next/image.tsx
// Render an HTMLImageElement in place of an Image component from `next/image`.
const Image: React.VFC = ({ ...props }) => {
  return <img {...props} />;
};

export default Image;

@RonNewcomb
Copy link

RonNewcomb commented Nov 28, 2022

I'm having the same issue with a plain img tag under nextjs. Using

import logoRectangle from 'assets/images/logo-rectangle.svg';

in

<img src={logoRectangle.src} height={32} alt={name} />

But in tests it still tries rendering from
/_next/static/media/logoRectangle.asdfasdf.svg
instead of
/__cypress/src/static/media/logoRectangle.asdfasdf.svg

Any ideas? I'm on version 11.2.0

@elmoghany
Copy link

elmoghany commented Feb 14, 2023

Same issue in v12.5.1 with edge, chrome & firefox!
I'm using react & nextjs
image

@sinukaarel
Copy link

I have resolved the image rendering issue the same way that @cdierkens described. I will add the changes that I have made here.

// cypress.config.ts
import path from 'path';

import { defineConfig } from 'cypress';
import { NormalModuleReplacementPlugin } from 'webpack';

export default defineConfig({
  component: {
    devServer: {
      framework: 'next',
      bundler: 'webpack',
      webpackConfig: {
        plugins: [
          new NormalModuleReplacementPlugin(
            /next\/image/,
            require.resolve(path.join(__dirname, 'cypress', 'mocks', 'next', 'image'))
          ),
        ],
      },
    },
  },
});

The URL-rewrite so that cypress can find those images happens in the mocked file.

The main idea is that Next optimizes images and therefore generates new URLs. You can see the GET request address has __next/static/... in the URL. So to rewrite this URL, we need to mock next/image import when we run Cypress tests. If the images are under the public folder, then those are bundled into the Cypress server. We can view these images by navigating to http://localhost:8080/assets in the Cypress browser. So we know those images are available. We just need to GET these images from the correct location.

This mock replaces the NextJs generated URL with a clean one to /assets/images and strips the extra part in the image filename between the image name and file extension. image.f6a92843.jpg -> image.jpg

// cypress/mocks/next/image.tsx
import type { ImageProps } from 'next/image';


/**
 * Converts the next/image static image URL to a regular path.
 *
 * Example:
 *
 * /_next/static/media/404.ea2b1f50.png -> /assets/images/404.png
 */
const convertURL = (url: string) => {
  return url
    .replace(/\/_next\/static\/media\//, '/assets/images/') // Use actual images location
    .replace(/(?<=\.)(.+)(?=png|jp?eg|tiff?|png|webp|bmp|gif)/, '');
};

const Image = (props: ImageProps) => {
  // Regular path to image resource
  if (typeof props.src === 'string') {
    return <img src={props.src} alt="image element" />;
  }

  let src: string;
  // StaticImageData - an import of image resource
  if ('src' in props.src) {
    src = props.src.src;
  } else {
    // StaticRequire
    src = props.src.default.src;
  }

  return <img src={convertURL(src)} alt="image element" />;
};

export default Image;

@lmiller1990
Copy link
Contributor

Some neat work-arounds here. Another alternative is described in our blog post, using cy.intercept().

I think a lot of this should be addressed in later Cypress versions, too. Has anyone tried Cy 10+ which should address at least some of the dev-server and Next.js compat issues?

@lmiller1990
Copy link
Contributor

There's a few work arounds and recommendations here. I don't think we will do anything in the immediate future outside of what we've currently got, unless someone has a compelling proposal (happy to reopen in that case).

For now no worked is planned on this, so I'll close it out.

@bsmithbeverley
Copy link

Hello, I appreciate this is closed now but I'd like to add another workaround/how I got this working in our project :)

We had a component that wrapped Next Image and passed it props from a third party CMS called Sanity. I managed to get the image mock loading using the following approach:

    cy.intercept('GET', '_next/image*', { fixture: 'test-image.jpg' }).as(
      'loadImage'
    )

    cy.mount(<SanityImage {...content} />)
    cy.wait('@loadImage')

   // Your tests/assertions etc

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
CT Issue related to component testing
Projects
None yet
Development

No branches or pull requests