Repository: vercel/next.js
File: test/e2e/app-dir/next-image/next-image.test.ts (TypeScript)

Code before:
import { nextTestSetup } from 'e2e-utils'

describe('app dir - next-image', () => {
  const { next, skipped } = nextTestSetup({
    files: __dirname,
    skipDeployment: true,
  })

  if (skipped) {
    return
  }

  describe('ssr content', () => {
    it('should handle HEAD requests for uncached images', async () => {
      // Run this test first to ensure the image cache is empty
      const $ = await next.render$('/')
      const imageUrl = $('#app-layout').attr('src')

      // With an empty cache, HEAD requests should return 200
      const headRes = await next.fetch(imageUrl, { method: 'HEAD' })
      expect(headRes.status).toBe(200)
      expect(headRes.headers.get('content-type')).toMatch(/^image\//)
    })

    it('should render images on / route', async () => {
      const $ = await next.render$('/')

      const layout = $('#app-layout')

      if (process.env.IS_TURBOPACK_TEST) {
        expect(layout.attr('src')).toMatchInlineSnapshot(
          `"/_next/image?url=%2F_next%2Fstatic%2Fmedia%2Ftest.4813cd24.png&w=828&q=85"`
        )
      } else {
        expect(layout.attr('src')).toMatchInlineSnapshot(
          `"/_next/image?url=%2F_next%2Fstatic%2Fmedia%2Ftest.3f1a293b.png&w=828&q=85"`
        )
      }

      if (process.env.IS_TURBOPACK_TEST) {
        expect(layout.attr('srcset')).toMatchInlineSnapshot(
          `"/_next/image?url=%2F_next%2Fstatic%2Fmedia%2Ftest.4813cd24.png&w=640&q=85 1x, /_next/image?url=%2F_next%2Fstatic%2Fmedia%2Ftest.4813cd24.png&w=828&q=85 2x"`
        )
      } else {
        expect(layout.attr('srcset')).toMatchInlineSnapshot(
          `"/_next/image?url=%2F_next%2Fstatic%2Fmedia%2Ftest.3f1a293b.png&w=640&q=85 1x, /_next/image?url=%2F_next%2Fstatic%2Fmedia%2Ftest.3f1a293b.png&w=828&q=85 2x"`
        )
      }

      const page = $('#app-page')

Review comment (suggestion):
I think you need to do two requests sequentially - one for HEAD followed by one for GET.

The HEAD should return 0 byte body and the GET should return the actual image in the body.

Code after:
import { nextTestSetup } from 'e2e-utils'
import fs from 'fs-extra'
import { join } from 'path'

describe('app dir - next-image', () => {
  const { next, skipped } = nextTestSetup({
    files: __dirname,
    skipDeployment: true,
  })

  if (skipped) {
    return
  }

  describe('ssr content', () => {
    it('should handle HEAD requests for uncached images', async () => {
      const imagesDir = join(next.testDir, '.next/cache/images')
      await fs.remove(imagesDir).catch(() => {})

      const $ = await next.render$('/')
      const imageUrl = $('#app-layout').attr('src')

      const headRes = await next.fetch(imageUrl, { method: 'HEAD' })
      expect(headRes.status).toBe(200)
      expect(headRes.headers.get('content-type')).toMatch(/^image\//)
      expect(headRes.headers.get('X-Nextjs-Cache')).toBe('MISS')

      const contentLength = headRes.headers.get('content-length')
      expect(Number(contentLength || '0')).toBeGreaterThan(0)
      const headBody = await headRes.arrayBuffer()
      expect(headBody.byteLength).toBe(0)

      const getRes = await next.fetch(imageUrl)
      expect(getRes.status).toBe(200)
      expect(getRes.headers.get('content-type')).toMatch(/^image\//)
      expect(getRes.headers.get('X-Nextjs-Cache')).toBe('HIT')

      const getContentLength = getRes.headers.get('content-length')
      expect(Number(getContentLength || '0')).toBeGreaterThan(0)

      const getBody = await getRes.arrayBuffer()
      expect(getBody.byteLength).toBeGreaterThan(0)
      expect(getBody.byteLength).toBe(Number(getContentLength))
    })

    it('should render images on / route', async () => {
      const $ = await next.render$('/')

      const layout = $('#app-layout')