@@ -9,7 +9,11 @@ import {
9
9
Observable ,
10
10
Observer ,
11
11
} from "../../../utilities/observables/Observable" ;
12
- import { ApolloLink , FetchResult } from "../../../link/core" ;
12
+ import {
13
+ ApolloLink ,
14
+ FetchResult ,
15
+ type RequestHandler ,
16
+ } from "../../../link/core" ;
13
17
import { InMemoryCache } from "../../../cache" ;
14
18
15
19
// mocks
@@ -31,7 +35,11 @@ import { wait } from "../../../testing/core";
31
35
import { ApolloClient } from "../../../core" ;
32
36
import { mockFetchQuery } from "../ObservableQuery" ;
33
37
import { Concast , print } from "../../../utilities" ;
34
- import { ObservableStream , spyOnConsole } from "../../../testing/internal" ;
38
+ import {
39
+ mockDeferStream ,
40
+ ObservableStream ,
41
+ spyOnConsole ,
42
+ } from "../../../testing/internal" ;
35
43
36
44
describe ( "ApolloClient" , ( ) => {
37
45
const getObservableStream = ( {
@@ -6522,6 +6530,129 @@ describe("ApolloClient", () => {
6522
6530
)
6523
6531
) . toBeUndefined ( ) ;
6524
6532
} ) ;
6533
+
6534
+ it ( "deduplicates queries as long as a query still has deferred chunks" , async ( ) => {
6535
+ const query = gql `
6536
+ query LazyLoadLuke {
6537
+ people(id: 1) {
6538
+ id
6539
+ name
6540
+ friends {
6541
+ id
6542
+ ... @defer {
6543
+ name
6544
+ }
6545
+ }
6546
+ }
6547
+ }
6548
+ ` ;
6549
+
6550
+ const outgoingRequestSpy = jest . fn ( ( ( operation , forward ) =>
6551
+ forward ( operation ) ) satisfies RequestHandler ) ;
6552
+ const defer = mockDeferStream ( ) ;
6553
+ const client = new ApolloClient ( {
6554
+ cache : new InMemoryCache ( { } ) ,
6555
+ link : new ApolloLink ( outgoingRequestSpy ) . concat ( defer . httpLink ) ,
6556
+ } ) ;
6557
+
6558
+ const query1 = new ObservableStream (
6559
+ client . watchQuery ( { query, fetchPolicy : "network-only" } )
6560
+ ) ;
6561
+ const query2 = new ObservableStream (
6562
+ client . watchQuery ( { query, fetchPolicy : "network-only" } )
6563
+ ) ;
6564
+ expect ( outgoingRequestSpy ) . toHaveBeenCalledTimes ( 1 ) ;
6565
+
6566
+ const initialData = {
6567
+ people : {
6568
+ __typename : "Person" ,
6569
+ id : 1 ,
6570
+ name : "Luke" ,
6571
+ friends : [
6572
+ {
6573
+ __typename : "Person" ,
6574
+ id : 5 ,
6575
+ } as { __typename : "Person" ; id : number ; name ?: string } ,
6576
+ {
6577
+ __typename : "Person" ,
6578
+ id : 8 ,
6579
+ } as { __typename : "Person" ; id : number ; name ?: string } ,
6580
+ ] ,
6581
+ } ,
6582
+ } ;
6583
+ const initialResult = {
6584
+ data : initialData ,
6585
+ loading : false ,
6586
+ networkStatus : 7 ,
6587
+ } ;
6588
+
6589
+ defer . enqueueInitialChunk ( {
6590
+ data : initialData ,
6591
+ hasNext : true ,
6592
+ } ) ;
6593
+
6594
+ await expect ( query1 ) . toEmitFetchResult ( initialResult ) ;
6595
+ await expect ( query2 ) . toEmitFetchResult ( initialResult ) ;
6596
+
6597
+ const query3 = new ObservableStream (
6598
+ client . watchQuery ( { query, fetchPolicy : "network-only" } )
6599
+ ) ;
6600
+ await expect ( query3 ) . toEmitFetchResult ( initialResult ) ;
6601
+ expect ( outgoingRequestSpy ) . toHaveBeenCalledTimes ( 1 ) ;
6602
+
6603
+ const firstChunk = {
6604
+ incremental : [
6605
+ {
6606
+ data : {
6607
+ name : "Leia" ,
6608
+ } ,
6609
+ path : [ "people" , "friends" , 0 ] ,
6610
+ } ,
6611
+ ] ,
6612
+ hasNext : true ,
6613
+ } ;
6614
+ const resultAfterFirstChunk = structuredClone ( initialResult ) ;
6615
+ resultAfterFirstChunk . data . people . friends [ 0 ] . name = "Leia" ;
6616
+
6617
+ defer . enqueueSubsequentChunk ( firstChunk ) ;
6618
+
6619
+ await expect ( query1 ) . toEmitFetchResult ( resultAfterFirstChunk ) ;
6620
+ await expect ( query2 ) . toEmitFetchResult ( resultAfterFirstChunk ) ;
6621
+ await expect ( query3 ) . toEmitFetchResult ( resultAfterFirstChunk ) ;
6622
+
6623
+ const query4 = new ObservableStream (
6624
+ client . watchQuery ( { query, fetchPolicy : "network-only" } )
6625
+ ) ;
6626
+ expect ( query4 ) . toEmitFetchResult ( resultAfterFirstChunk ) ;
6627
+ expect ( outgoingRequestSpy ) . toHaveBeenCalledTimes ( 1 ) ;
6628
+
6629
+ const secondChunk = {
6630
+ incremental : [
6631
+ {
6632
+ data : {
6633
+ name : "Han Solo" ,
6634
+ } ,
6635
+ path : [ "people" , "friends" , 1 ] ,
6636
+ } ,
6637
+ ] ,
6638
+ hasNext : false ,
6639
+ } ;
6640
+ const resultAfterSecondChunk = structuredClone ( resultAfterFirstChunk ) ;
6641
+ resultAfterSecondChunk . data . people . friends [ 1 ] . name = "Han Solo" ;
6642
+
6643
+ defer . enqueueSubsequentChunk ( secondChunk ) ;
6644
+
6645
+ await expect ( query1 ) . toEmitFetchResult ( resultAfterSecondChunk ) ;
6646
+ await expect ( query2 ) . toEmitFetchResult ( resultAfterSecondChunk ) ;
6647
+ await expect ( query3 ) . toEmitFetchResult ( resultAfterSecondChunk ) ;
6648
+ await expect ( query4 ) . toEmitFetchResult ( resultAfterSecondChunk ) ;
6649
+
6650
+ const query5 = new ObservableStream (
6651
+ client . watchQuery ( { query, fetchPolicy : "network-only" } )
6652
+ ) ;
6653
+ expect ( query5 ) . not . toEmitAnything ( ) ;
6654
+ expect ( outgoingRequestSpy ) . toHaveBeenCalledTimes ( 2 ) ;
6655
+ } ) ;
6525
6656
} ) ;
6526
6657
6527
6658
describe ( "missing cache field warnings" , ( ) => {
0 commit comments