@@ -500,6 +500,49 @@ function runTests({
500
500
)
501
501
await expectWidth ( res , w )
502
502
} )
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
+ } )
503
546
}
504
547
505
548
it ( 'should fail when url has file protocol' , async ( ) => {
@@ -532,14 +575,15 @@ function runTests({
532
575
} )
533
576
}
534
577
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 ( ) => {
536
579
await fs . remove ( imagesDir )
537
580
538
581
const query = { url : '/test.png' , w, q : 80 }
539
582
const opts = { headers : { accept : 'image/webp' } }
540
583
541
584
const res1 = await fetchViaHTTP ( appPort , '/_next/image' , query , opts )
542
585
expect ( res1 . status ) . toBe ( 200 )
586
+ expect ( res1 . headers . get ( 'X-Nextjs-Cache' ) ) . toBe ( 'MISS' )
543
587
expect ( res1 . headers . get ( 'Content-Type' ) ) . toBe ( 'image/webp' )
544
588
expect ( res1 . headers . get ( 'Content-Disposition' ) ) . toBe (
545
589
`inline; filename="test.webp"`
@@ -549,6 +593,7 @@ function runTests({
549
593
550
594
const res2 = await fetchViaHTTP ( appPort , '/_next/image' , query , opts )
551
595
expect ( res2 . status ) . toBe ( 200 )
596
+ expect ( res2 . headers . get ( 'X-Nextjs-Cache' ) ) . toBe ( 'HIT' )
552
597
expect ( res2 . headers . get ( 'Content-Type' ) ) . toBe ( 'image/webp' )
553
598
expect ( res2 . headers . get ( 'Content-Disposition' ) ) . toBe (
554
599
`inline; filename="test.webp"`
@@ -561,6 +606,7 @@ function runTests({
561
606
await waitFor ( ttl * 1000 )
562
607
const res3 = await fetchViaHTTP ( appPort , '/_next/image' , query , opts )
563
608
expect ( res3 . status ) . toBe ( 200 )
609
+ expect ( res3 . headers . get ( 'X-Nextjs-Cache' ) ) . toBe ( 'STALE' )
564
610
expect ( res3 . headers . get ( 'Content-Type' ) ) . toBe ( 'image/webp' )
565
611
expect ( res3 . headers . get ( 'Content-Disposition' ) ) . toBe (
566
612
`inline; filename="test.webp"`
@@ -579,6 +625,7 @@ function runTests({
579
625
580
626
const res1 = await fetchViaHTTP ( appPort , '/_next/image' , query , opts )
581
627
expect ( res1 . status ) . toBe ( 200 )
628
+ expect ( res1 . headers . get ( 'X-Nextjs-Cache' ) ) . toBe ( 'MISS' )
582
629
expect ( res1 . headers . get ( 'Content-Type' ) ) . toBe ( 'image/svg+xml' )
583
630
expect ( res1 . headers . get ( 'Content-Disposition' ) ) . toBe (
584
631
`inline; filename="test.svg"`
@@ -588,6 +635,7 @@ function runTests({
588
635
589
636
const res2 = await fetchViaHTTP ( appPort , '/_next/image' , query , opts )
590
637
expect ( res2 . status ) . toBe ( 200 )
638
+ expect ( res2 . headers . get ( 'X-Nextjs-Cache' ) ) . toBe ( 'HIT' )
591
639
expect ( res2 . headers . get ( 'Content-Type' ) ) . toBe ( 'image/svg+xml' )
592
640
expect ( res2 . headers . get ( 'Content-Disposition' ) ) . toBe (
593
641
`inline; filename="test.svg"`
@@ -604,6 +652,7 @@ function runTests({
604
652
605
653
const res1 = await fetchViaHTTP ( appPort , '/_next/image' , query , opts )
606
654
expect ( res1 . status ) . toBe ( 200 )
655
+ expect ( res1 . headers . get ( 'X-Nextjs-Cache' ) ) . toBe ( 'MISS' )
607
656
expect ( res1 . headers . get ( 'Content-Type' ) ) . toBe ( 'image/gif' )
608
657
expect ( res1 . headers . get ( 'Content-Disposition' ) ) . toBe (
609
658
`inline; filename="animated.gif"`
@@ -613,6 +662,7 @@ function runTests({
613
662
614
663
const res2 = await fetchViaHTTP ( appPort , '/_next/image' , query , opts )
615
664
expect ( res2 . status ) . toBe ( 200 )
665
+ expect ( res2 . headers . get ( 'X-Nextjs-Cache' ) ) . toBe ( 'HIT' )
616
666
expect ( res2 . headers . get ( 'Content-Type' ) ) . toBe ( 'image/gif' )
617
667
expect ( res2 . headers . get ( 'Content-Disposition' ) ) . toBe (
618
668
`inline; filename="animated.gif"`
@@ -810,6 +860,16 @@ function runTests({
810
860
811
861
const json1 = await fsToJson ( imagesDir )
812
862
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
+ }
813
873
} )
814
874
815
875
if ( isDev || isSharp ) {
0 commit comments