next.js/test/e2e/app-dir/expire-time/expire-time.test.ts
expire-time.test.ts64 lines2.5 KB
import { nextTestSetup } from 'e2e-utils'
import { retry } from 'next-test-utils'

describe('expire-time', () => {
  const { next, isNextDeploy } = nextTestSetup({
    files: __dirname,
  })

  // On Vercel, ISR cache decisions happen at the Proxy layer. The Vercel
  // builder reads the route's `expire` value (Next.js's `initialExpireSeconds`
  // in the prerender manifest, which the build derives from `expireTime` when
  // no explicit `cacheLife` expire is set) and passes it to the Proxy as
  // `staleExpiration`. The Proxy is also expected, once implemented, to read
  // updated values from Next.js's `stale-while-revalidate` response header on
  // subsequent revalidations. Today the Proxy ignores the expire value entirely
  // and treats it as one year. Past `expireTime` it serves stale with a
  // background revalidation instead of a blocking prerender. When Proxy is
  // updated to honor the expire value, this test will start passing in deploy
  // mode and `it.failing` will itself fail. That's the signal to flip it back
  // to `it`.
  const itFailsWhenDeployed = isNextDeploy ? it.failing : it

  /* eslint-disable jest/no-standalone-expect */
  itFailsWhenDeployed(
    'should do a blocking revalidation when the cache entry has expired',
    async () => {
      const $first = await next.render$('/')
      const v1 = $first('#value').text()
      expect(v1).toBeDateString()

      // The first request might trigger a background revalidation if the
      // prerender document is already older than the configured revalidate
      // time. So we refetch until we get a different value than the first one.
      let v2: string
      await retry(
        async () => {
          const $second = await next.render$('/')
          v2 = $second('#value').text()
          expect(v2).toBeDateString()
          expect(v2).not.toBe(v1)
        },
        4_000,
        200
      )

      // Wait past the `expireTime` (10 s). The next request must trigger a
      // blocking prerender, not stale-while-revalidate — so the response
      // returned right here carries a freshly-computed value.
      await new Promise((resolve) => setTimeout(resolve, 10_000))

      const $third = await next.render$('/')
      const v3 = $third('#value').text()
      expect(v3).toBeDateString()

      // This should be a new value, not the expired previous one, and
      // especially not the expired prerendered one.
      expect(v3).not.toBe(v2)
      expect(v3).not.toBe(v1)
      console.log({ v1, v2, v3 })
    }
  )
  /* eslint-enable jest/no-standalone-expect */
})
Quest for Codev2.0.0
/
SIGN IN