@@ -500,6 +500,49 @@ function runTests({
500500 )
501501 await expectWidth ( res , w )
502502 } )
503+
504+ it ( 'should use cache and stale-while-revalidate when query is the same for external image' , async ( ) => {
505+ await fs . remove ( imagesDir )
506+
507+ const url = 'https://image-optimization-test.vercel.app/test.jpg'
508+ const query = { url, w, q : 39 }
509+ const opts = { headers : { accept : 'image/webp' } }
510+
511+ const res1 = await fetchViaHTTP ( appPort , '/_next/image' , query , opts )
512+ expect ( res1 . status ) . toBe ( 200 )
513+ expect ( res1 . headers . get ( 'X-Nextjs-Cache' ) ) . toBe ( 'MISS' )
514+ expect ( res1 . headers . get ( 'Content-Type' ) ) . toBe ( 'image/webp' )
515+ expect ( res1 . headers . get ( 'Content-Disposition' ) ) . toBe (
516+ `inline; filename="test.webp"`
517+ )
518+ const json1 = await fsToJson ( imagesDir )
519+ expect ( Object . keys ( json1 ) . length ) . toBe ( 1 )
520+
521+ const res2 = await fetchViaHTTP ( appPort , '/_next/image' , query , opts )
522+ expect ( res2 . status ) . toBe ( 200 )
523+ expect ( res2 . headers . get ( 'X-Nextjs-Cache' ) ) . toBe ( 'HIT' )
524+ expect ( res2 . headers . get ( 'Content-Type' ) ) . toBe ( 'image/webp' )
525+ expect ( res2 . headers . get ( 'Content-Disposition' ) ) . toBe (
526+ `inline; filename="test.webp"`
527+ )
528+ const json2 = await fsToJson ( imagesDir )
529+ expect ( json2 ) . toStrictEqual ( json1 )
530+
531+ if ( ttl ) {
532+ // Wait until expired so we can confirm image is regenerated
533+ await waitFor ( ttl * 1000 )
534+ const res3 = await fetchViaHTTP ( appPort , '/_next/image' , query , opts )
535+ expect ( res3 . status ) . toBe ( 200 )
536+ expect ( res3 . headers . get ( 'X-Nextjs-Cache' ) ) . toBe ( 'STALE' )
537+ expect ( res3 . headers . get ( 'Content-Type' ) ) . toBe ( 'image/webp' )
538+ expect ( res3 . headers . get ( 'Content-Disposition' ) ) . toBe (
539+ `inline; filename="test.webp"`
540+ )
541+ const json3 = await fsToJson ( imagesDir )
542+ expect ( json3 ) . not . toStrictEqual ( json1 )
543+ expect ( Object . keys ( json3 ) . length ) . toBe ( 1 )
544+ }
545+ } )
503546 }
504547
505548 it ( 'should fail when url has file protocol' , async ( ) => {
@@ -532,14 +575,15 @@ function runTests({
532575 } )
533576 }
534577
535- it ( 'should use cached image file when parameters are the same' , async ( ) => {
578+ it ( 'should use cache and stale-while-revalidate when query is the same for internal image ' , async ( ) => {
536579 await fs . remove ( imagesDir )
537580
538581 const query = { url : '/test.png' , w, q : 80 }
539582 const opts = { headers : { accept : 'image/webp' } }
540583
541584 const res1 = await fetchViaHTTP ( appPort , '/_next/image' , query , opts )
542585 expect ( res1 . status ) . toBe ( 200 )
586+ expect ( res1 . headers . get ( 'X-Nextjs-Cache' ) ) . toBe ( 'MISS' )
543587 expect ( res1 . headers . get ( 'Content-Type' ) ) . toBe ( 'image/webp' )
544588 expect ( res1 . headers . get ( 'Content-Disposition' ) ) . toBe (
545589 `inline; filename="test.webp"`
@@ -549,6 +593,7 @@ function runTests({
549593
550594 const res2 = await fetchViaHTTP ( appPort , '/_next/image' , query , opts )
551595 expect ( res2 . status ) . toBe ( 200 )
596+ expect ( res2 . headers . get ( 'X-Nextjs-Cache' ) ) . toBe ( 'HIT' )
552597 expect ( res2 . headers . get ( 'Content-Type' ) ) . toBe ( 'image/webp' )
553598 expect ( res2 . headers . get ( 'Content-Disposition' ) ) . toBe (
554599 `inline; filename="test.webp"`
@@ -561,6 +606,7 @@ function runTests({
561606 await waitFor ( ttl * 1000 )
562607 const res3 = await fetchViaHTTP ( appPort , '/_next/image' , query , opts )
563608 expect ( res3 . status ) . toBe ( 200 )
609+ expect ( res3 . headers . get ( 'X-Nextjs-Cache' ) ) . toBe ( 'STALE' )
564610 expect ( res3 . headers . get ( 'Content-Type' ) ) . toBe ( 'image/webp' )
565611 expect ( res3 . headers . get ( 'Content-Disposition' ) ) . toBe (
566612 `inline; filename="test.webp"`
@@ -579,6 +625,7 @@ function runTests({
579625
580626 const res1 = await fetchViaHTTP ( appPort , '/_next/image' , query , opts )
581627 expect ( res1 . status ) . toBe ( 200 )
628+ expect ( res1 . headers . get ( 'X-Nextjs-Cache' ) ) . toBe ( 'MISS' )
582629 expect ( res1 . headers . get ( 'Content-Type' ) ) . toBe ( 'image/svg+xml' )
583630 expect ( res1 . headers . get ( 'Content-Disposition' ) ) . toBe (
584631 `inline; filename="test.svg"`
@@ -588,6 +635,7 @@ function runTests({
588635
589636 const res2 = await fetchViaHTTP ( appPort , '/_next/image' , query , opts )
590637 expect ( res2 . status ) . toBe ( 200 )
638+ expect ( res2 . headers . get ( 'X-Nextjs-Cache' ) ) . toBe ( 'HIT' )
591639 expect ( res2 . headers . get ( 'Content-Type' ) ) . toBe ( 'image/svg+xml' )
592640 expect ( res2 . headers . get ( 'Content-Disposition' ) ) . toBe (
593641 `inline; filename="test.svg"`
@@ -604,6 +652,7 @@ function runTests({
604652
605653 const res1 = await fetchViaHTTP ( appPort , '/_next/image' , query , opts )
606654 expect ( res1 . status ) . toBe ( 200 )
655+ expect ( res1 . headers . get ( 'X-Nextjs-Cache' ) ) . toBe ( 'MISS' )
607656 expect ( res1 . headers . get ( 'Content-Type' ) ) . toBe ( 'image/gif' )
608657 expect ( res1 . headers . get ( 'Content-Disposition' ) ) . toBe (
609658 `inline; filename="animated.gif"`
@@ -613,6 +662,7 @@ function runTests({
613662
614663 const res2 = await fetchViaHTTP ( appPort , '/_next/image' , query , opts )
615664 expect ( res2 . status ) . toBe ( 200 )
665+ expect ( res2 . headers . get ( 'X-Nextjs-Cache' ) ) . toBe ( 'HIT' )
616666 expect ( res2 . headers . get ( 'Content-Type' ) ) . toBe ( 'image/gif' )
617667 expect ( res2 . headers . get ( 'Content-Disposition' ) ) . toBe (
618668 `inline; filename="animated.gif"`
@@ -810,6 +860,16 @@ function runTests({
810860
811861 const json1 = await fsToJson ( imagesDir )
812862 expect ( Object . keys ( json1 ) . length ) . toBe ( 1 )
863+
864+ const xCache1 = res1 . headers . get ( 'X-Nextjs-Cache' )
865+ const xCache2 = res2 . headers . get ( 'X-Nextjs-Cache' )
866+ if ( xCache1 === 'HIT' ) {
867+ expect ( xCache1 ) . toBe ( 'HIT' )
868+ expect ( xCache2 ) . toBe ( 'MISS' )
869+ } else {
870+ expect ( xCache1 ) . toBe ( 'MISS' )
871+ expect ( xCache2 ) . toBe ( 'HIT' )
872+ }
813873 } )
814874
815875 if ( isDev || isSharp ) {
0 commit comments