next.js/test/e2e/app-dir/next-image-events/next-image-events.test.ts
next-image-events.test.ts148 lines4.4 KB
import { nextTestSetup } from 'e2e-utils'
import { retry } from 'next-test-utils'

describe('next-image-events', () => {
  const { next } = nextTestSetup({
    files: __dirname,
  })

  it('should not call onLoad multiple times', async () => {
    const imageRequests = []
    const browser = await next.browser('/fulfilled', {
      beforePageLoad(page) {
        page.on('request', (request) => {
          if (request.resourceType() === 'image') {
            imageRequests.push(request.url())
          }
        })
      },
    })

    let logsIdx = 0
    await retry(async () => {
      const logs = await browser.log()
      expect(
        logs.slice(logsIdx).filter(({ source }) => source === 'error')
      ).toEqual([
        {
          source: 'error',
          message: 'hydrated image load',
        },
      ])
      logsIdx = logs.length
    })
    expect(imageRequests).toEqual([expect.stringContaining('test')])
    imageRequests.length = 0

    await browser.locator(':text("Show Client image")').click()

    await retry(async () => {
      const logs = await browser.log()
      expect(
        logs.slice(logsIdx).filter(({ source }) => source === 'error')
      ).toEqual([
        {
          source: 'error',
          message: 'client rendered image load',
        },
      ])
      logsIdx = logs.length
    })
    expect(imageRequests).toEqual([expect.stringContaining('test')])
    imageRequests.length = 0

    await browser.locator(':text("rerender Page")').click()

    const logs = await browser.log()
    expect(
      logs.slice(logsIdx).filter(({ source }) => source === 'error')
    ).toEqual([])
    expect(imageRequests).toEqual([])
    imageRequests.length = 0
  })

  it('should not infinitely retry on error', async () => {
    const nextImageRequests: string[] = []
    let logsIdx = 0
    const browser = await next.browser('/rejected', {
      beforePageLoad(page) {
        // We're manually aborting here to simulate an error before React hydrates.
        // A real request might settle too late to test this behavior reliably.
        // Especially in dev, requests (even if warm) may take longer than hydration.
        page.route(
          /\/will-never-exist\.png|\/still-doesnt-exist\.png/,
          (route) => {
            nextImageRequests.push(route.request().url())
            return route.abort()
          }
        )
      },
    })

    await retry(async () => {
      const logs = await browser.log()
      expect(
        logs.slice(logsIdx).filter(({ source }) => source === 'error')
      ).toEqual([
        {
          source: 'error',
          message: 'Failed to load resource: net::ERR_FAILED',
        },
        // Next.js retries once to trigger onError on SSRed, settled images.
        // If this test fails, we'll either hydrated faster than the request settled,
        // or dropped the retrying behavior of next/image
        {
          source: 'error',
          message: 'Failed to load resource: net::ERR_FAILED',
        },
        {
          source: 'error',
          message: 'hydrated image error',
        },
      ])
      logsIdx = logs.length
    })
    expect(nextImageRequests).toEqual([
      expect.stringContaining('will-never-exist'),
      // Next.js retries once to trigger onError on SSRed, settled images.
      // If this test fails, we'll either hydrated faster than the request settled,
      // or dropped the retrying behavior of next/image
      expect.stringContaining('will-never-exist'),
    ])
    nextImageRequests.length = 0

    await browser.locator(':text("Show Client image")').click()

    await retry(async () => {
      const logs = await browser.log()
      expect(
        logs.slice(logsIdx).filter(({ source }) => source === 'error')
      ).toEqual([
        {
          source: 'error',
          message: 'Failed to load resource: net::ERR_FAILED',
        },
        {
          source: 'error',
          message: 'client rendered image error',
        },
      ])
      logsIdx = logs.length
    })
    expect(nextImageRequests).toEqual([
      expect.stringContaining('still-doesnt-exist'),
    ])
    nextImageRequests.length = 0

    await browser.locator(':text("rerender Page")').click()

    const logs = await browser.log()
    expect(
      logs.slice(logsIdx).filter(({ source }) => source === 'error')
    ).toEqual([])
    expect(nextImageRequests).toEqual([])
    nextImageRequests.length = 0
    logsIdx = logs.length
  })
})
Quest for Codev2.0.0
/
SIGN IN